Skip to content

Mink Tests

Introduction

The Mink framework lets you automate running the Chrome browser to run tests against CiviCRM the same way a user would click through a site. It has an advantage over some javascript-only browser automation frameworks because at the same time you also have access to all the usual backend and API functions you would normally have under plain phpunit. A disadvantage is that the download is more complicated and it can sometimes require resolving conflicting dependencies while downloading/upgrading.

Mink integration with CiviCRM is available starting in version 5.34.0.

Installation

  1. Create an empty testing MySQL database and a test MySQL user.
  2. Download the files.
    • While it downloads Drupal files onto your system, you don't need to know anything about installing or using Drupal. The Mink integration with the CMS is only currently available for Drupal 8+, so those files need to be present. For more details on these commands see the Drupal 8 install documentation.
      export COMPOSER_MEMORY_LIMIT=-1
      # Replace `my_folder` with a new folder name that will get created to store the files.
      # For choosing a location, note that in a later step you will need to make the web subfolder visible to your local webserver.
      composer create-project drupal/recommended-project my_folder
      cd my_folder
      composer config extra.enable-patching true
      composer config minimum-stability dev
      composer require drupal/core-dev
      composer require phpspec/prophecy-phpunit:'^2'
      composer require civicrm/civicrm-asset-plugin:'~1.1'
      # replace `version_string` with a CiviCRM version, e.g. `~5.34` or `dev-master`
      composer require civicrm/civicrm-{core,packages,drupal-8}:'version_string'
      
  3. Configure your local webserver to have a domain with document root pointing to the my_folder/web subfolder.
    • For the domain name you may have some cookie problems with just localhost, so you can use a domain like mink.localhost.
    • You can test if the web page loads if you like, but do NOT proceed to install Drupal.
  4. Copy web/core/phpunit.xml.dist to the top level folder, beside web and vendor, and rename it phpunit.xml.
    • You can put it inside web/core which will make some steps slightly easier and require less ../.. in paths, but then the file can get removed on upgrade.
  5. Configure a few variables inside phpunit.xml.
    • Update the path for bootstrap.php so that it's relative to where you have put phpunit.xml. The original value is relative to phpunit.xml.dist, in web/core.
    • The main variables to set are SIMPLETEST_BASE_URL, SIMPLETEST_DB, and BROWSERTEST_OUTPUT_DIRECTORY.
    • See configure phpunit for more details.
  6. Install Google Chrome if you don't already have it installed.
  7. Check the version of Chrome by choosing Help - About Chrome from the menu.
  8. Download a matching version of chromedriver from the chromedriver website.
    • You can put chromedriver in any folder. You just need to be able start it running before running any tests.

Writing your first test

  1. File, class, and function naming follow the same rules as for regular phpunit tests. Put the following in a file somewhere.
    <?php
    use Drupal\Tests\civicrm\FunctionalJavascript\CiviCrmTestBase;
    
    class Test1Test extends CiviCrmTestBase {
    
      public function test1() {
        $account = $this->createUser([
          'administer CiviCRM',
          'access CiviCRM',
          'administer CiviCRM system',
          'administer CiviCRM data',
        ]);
        $this->drupalLogin($account);
        $this->drupalGet('civicrm/admin/contribute?reset=1');
        $this->assertSession()->pageTextContains('Add Contribution Page');
      }
    
    }
    
  2. This simply creates a user with some permissions, logs in, and then visits the contribution dashboard.

Running tests - command line

  1. In a separate terminal window run chromedriver --port=4444
  2. In your original or another terminal window, cd my_folder/web/core
  3. ../../vendor/bin/phpunit -c ../../phpunit.xml path/to/your/Test1Test.php
  4. It should take a minute to get started, but then you should see Chrome open. Resist the urge to click the install button and just watch. You should see it eventually log the user in and then visit the Contribution dashboard. Then it should close Chrome and display a green "OK" message in the terminal.
  5. There will be some screenshots in the folder you set for BROWSERTEST_OUTPUT_DIRECTORY earlier.

Environment differences from CiviCRM's test framework

The main differences are:

  1. Your class can't also extend CiviUnitTestCase so you won't have access to any helper functions defined there. But your class does extend PHPUnit so you have all the usual assert functions like assertEquals(), assertCount(), etc.
  2. Until setUp() runs CiviCRM is not installed yet. This means that the functions that you're familiar with that live in traits, like callAPISuccess() or individualCreate(), can't be made available by use'ing the trait in your class, since they get compiled too early.
  3. Currently it does not load the sample data.
  4. Running a test that lives in an extension that you're working on at the same time as you're running that test needs to be set up a little differently than usual. See below.

Accessing CiviCRM APIs

Once setUp() runs, regular API calls are available, e.g.

function testSomething() {
  require_once 'api/api.php';
  $result = civicrm_api3('Contact', 'getsingle', ['id' => 1]);
}

OR

function testSomething() {
  $contact = \Civi\Api4\Contact::get(FALSE)->addWhere('id', '=', 1)->execute()->first();
}

Console output

You can temporarily debug with var_dump() or other functions the same as with CiviCRM tests, but note that in this environment any output is considered a fail and will end the test early.

Testing an extension from within that extension

The tests create a random temporary "simpletest" subfolder where some of the test data lives. It doesn't exist before the tests start and is removed after. In particular, the default location for CiviCRM extensions lies within this subfolder, so CiviCRM won't be able to find your extension. One workaround is to read a variable from phpunit.xml that points to your extensions directory and then uses it to update CiviCRM. You need to do this after the parent setUp() runs. A full example of the code below is in this extension.

    // Set the path to your extensions directory in phpunit.xml with <env name="DEV_EXTENSION_DIR" value="path_to_ext_folder"/>
    if ($extdir = getenv('DEV_EXTENSION_DIR')) {
      \Civi::settings()->set('extensionsDir', $extdir);
      \CRM_Core_Config::singleton(TRUE, TRUE);
      \CRM_Extension_System::setSingleton(new \CRM_Extension_System());
    }

    require_once 'api/api.php';
    civicrm_api3('Extension', 'install', ['keys' => 'myextension']);
    // Required
    drupal_flush_all_caches();

    // Need this otherwise any new permissions won't be available yet.
    unset(\Civi::$statics['CRM_Core_Permission']['basicPermissions']);

Further Reading

Troubleshooting

  • You get "THE ERROR HANDLER HAS CHANGED!" and no tests run.
    • You forgot to start chromedriver, didn't you?
  • Something failed and now all subsequent runs say "RuntimeException: The provided database connection in SIMPLETEST_DB contains CiviCRM tables, use a different database."
    • If it wasn't able to clean up properly, you'll have a half-filled database. In order to prevent accidentally overwriting a real database the tests won't run until you've cleaned up. Since it's a test database, just drop and recreate it.
  • What are all these symfony deprecation notices?
    • For the near term every test is going to have those. If you can submit a patch to core please do so.