Change record status: 
Introduced in branch: 
Introduced in version: 

As part of a larger initiative to modernize the testing system and move to PHPUnit as the runner for all of our tests, BrowserTestBase and JavascriptTestBase have been added to core.

These are Mink based browser implementations using the Goutte-based driver (Goutte (friendsofphp/goutte) is basically guzzle/guzzlehttp and symfony/domcrawler). This allows us to use industry standard PHP libraries for simulating a browser and opens the door for further steps, eg. #2469717: Step 3: Create a KernelWebTestBase base class/driver of the planned testing roadmap.

When should you use the new BrowserTestBase and JavascriptTestBase?


You are free to use them for your contrib projects, see #2469609: Convert all WebTestBase to BrowserTestBase for example.


For new tests, use the new base classes. For older tests, see #2807237: PHPUnit initiative for the larger conversion process.

Kudos to Simpletest

Simpletest has provided us with the means to make the wide-ranging changes we've made in Drupal 8. Without a test-framework we'd have been flying blind performing the level of refactoring we've done in Drupal 8. But throughout the Drupal 8 cycle, poor old Simpletest hasn't gotten the same level of love as other parts of core. This is part of the plan to spread some love to our testing APIs, modernize them and bring them in to line with the wider PHP community.

But my contrib tests are written with Simpletest!

Don't worry, Simpletest will need to be supported for Drupal 8, removing it would be an API break.

Converting your contrib tests

  1. Update your class to extend from \Drupal\Tests\BrowserTestBase or \Drupal\FunctionalJavascriptTests\JavascriptTestBase
  2. Move your test from the src/Tests folder (Drupal\yourmodule\Tests namespace) to the tests/src/Functional (Drupal\Tests\yourmodule\Functional namespace)
    or tests/src/FunctionalJavascript (Drupal\Tests\yourmodule\FunctionalJavascript namespace) for javascript tests.
  3. Update your asserts as per the table
Before (Simpletest/WebTestBase) After (Phpunit/Mink)
assertEqual assertEquals
assertIdentical assertSame
drupalPostForm($url, $values, $button_text); submitForm($values, $button_text);
assertRaw($text) assertSession()->pageTextContains($text)
assertField($name) assertSession()->fieldExists($name)
assertFieldByXPath($xpath) assertSession()->fieldExistsByXpath($xpath)

For more examples of asserts, see the doxygen for the WebAssert object.
For more examples of controlling the browser with Mink (eg navigation, form manipulation) see the doxygen for the Mink session.

Running your contrib tests

$ cd core
$ sudo -u www-data SIMPLETEST_BASE_URL=http://yourd8.url ./vendor/bin/phpunit --testsuite functional --group yourmodule
# Or with run-test
$ sudo -u www-data php ./core/scripts/ --url http://yourd8.url --class "Drupal\Tests\yourmodule\Functional\YourModuleTest"

Running tests

With that change we have now both a very fast test suite ("unit") and a slower one ("functional").
If you just use:


both will be executed.

For development you probably want to separate the execution, so to execute both in isolation use:

./vendor/bin/phpunit --testsuite unit

as well as

./vendor/bin/phpunit --testsuite functional

Read more information on writing and running JavascriptTestBase tests

Module developers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other updates done


jonathan1055’s picture

I have found a few other things which need minor adjustment when converting tests from WebTestBase to BrowserTestBase:

Before (Simpletest/WebTestBase) After (BrowserTestBase/Phpunit/Mink)
assertHeader() assertSession()->responseHeaderEquals()
assertFieldById(field-id, '', 'message') assertFieldById(field-id, NULL, 'message')
(string)$xpath[0] $xpath[0]->getText()

For assertFieldById() and ByName() and their 'NoField' equivalents, the second optional parameter is the field value to check for. In WTB the empty string '' is interpreted as 'ignore the field value' but in BTB the empty string is a valid value to check for. If you are not interested in the field value you need to use NULL for the second parameter.

When using an xpath match, in WTB the value could be converted to a string by casting as (string). In BTB this fails and you must use the ->getText() method.