Commerce 2.x: Unit, Kernel, and Functional Tests Oh My!
At the end of May, I made an initiative to move all of the Drupal Commerce tests away from Simpletest and to use the available test classes built off of PHPUnit. Why? Simpletest is a test framework within Drupal and not used by the PHP community at large.
Death to Simpletest, Long Live PHPUnit, Mink, and PhantomJS
How hard is it?
We have plenty of commits with examples of porting our tests from Simpletest to Kernel or BrowserTest tests. However, the EntitySelectWidgetTest is probably the most straightforward and shows how easy it is to migrate. See the commit and diff here:
This wasn't a lone effort. Shout out to those who helped ensure we have a well tested project:
- ThinkBean for sponsoring my Commerce 2.x, which helped get this initiative wrapped up.
- Shawn McCabe at Acro Media for working on the product tests and the other remaining tests.
With Simpletest, there were two test suites (kind of): web test and unit test. However, I do not recall seeing any of the latter in the wild because it could not communicate with the database.
In Drupal 8 we now have four different test suites, each with specific purposes.
- Unit: You are testing class functionality, and mock other required services. See DefaultStoreResolverTest as an example.
- Kernel: Think of a Kernel test as a being a Unit test which has the ability to install schema and config to the database. You do not need to mock service objects when using a Kernel test. See ProductTest (covers product entity.) as an example
- Functional: Functional tests are tests which require a fully bootstrapped Drupal and provide browser emulation via Mink. This test type will look familiar to those who have worked with Simpletest. See StoreTypeTest as an example.
Tests live in the Drupal\Tests\$module\$type namespace (e.g. Drupal\Tests\commerce\Kernel) and tests/src/$type folder (e.g. tests/src/Kernel).
We will cover how to setup PhpStorm to run Drupal Commerce's tests. PhpStorm provides a PHPUnit test runner and lets you stay within your workbench to run tests. I run my tests using PHP's built-in server and SQLite. This allows you to run tests with a minimal environment setup.
PhpStorm professional? Here's the tl;dr configurations to put in your project: https://gist.github.com/mglaman/f0cbe19a2fae4fb7f685c25606ab60ea
If you are new to all of this, you will also want to check out the Running PHPUnit tests handbook page on Drupal.org: https://www.drupal.org/node/2116263
The first step is to copy the phpunit.xml.dist shipped with Drupal. Make a copy and name it phpunit.xml. There are three values you will want to adjust in the
<php> section of the configuration file.
- SIMPLETEST_BASE_URL: The URL the Drupal site can be viewed at.
- SIMPLETEST_DB: If the site is not installed, or is different than the default database connection.
You can use SQLite for testing, which is my primary use case for setting SIMPLETEST_DB. Below is a sample of what the configuration changes would look for the environment settings.
<!-- Snippet of environment settings. -->
<!-- Set error reporting to E_ALL. -->
<ini name="error_reporting" value="32767"/>
<!-- Do not limit the amount of memory tests take to run. -->
<ini name="memory_limit" value="-1"/>
<!-- Example SIMPLETEST_BASE_URL value: http://localhost -->
<env name="SIMPLETEST_BASE_URL" value="http://localhost:8080"/>
<!-- Example SIMPLETEST_DB value: mysql://username:[email protected]/databasename#table_prefix -->
<env name="SIMPLETEST_DB" value="sqlite://localhost/sites/default/files/.ht.sqlite"/>
<!-- Example BROWSERTEST_OUTPUT_DIRECTORY value: /path/to/webroot/sites/simpletest/browser_output -->
<!--<env name="BROWSERTEST_OUTPUT_DIRECTORY" value="/var/platform/www/sites/simpletest/browser_output"/>-->
<env name="BROWSERTEST_OUTPUT_DIRECTORY" value="/Users/mglaman/Drupal/sites/commerce2x/www/sites/simpletest/browser_output"/>
<ini name="display_errors" value="On" />
<ini name="display_startup_errors" value="On" />
Setup PHPUnit Runner
The first step is to create a PHPUnit Runner. Visit Run -> Edit Configurations and create a new PHPUnit runner. You will want to have it configured to look for tests inside of modules/commerce (or wherever you put Drupal Commerce, e.g. modules/contrib/commerce.) Then change the configuration to be base on the one you created previously, such as core/phpunit.xml.
Drupal expects PhantomJS to run on a particular port with seeded configuration. The IntelliJ platform does not have a PhantomJS plugin yet, so I recommend writing a Bash script, and then a Bash runner. Note: You will need to install the BashSupport plugin in order to have this runner.
phantomjs --ssl-protocol=any --ignore-ssl-errors=true ./vendor/jcalderonzumba/gastonjs/src/Client/main.js 8510 1024 768
The above line starts PhantomJS as the Drupal tests assume it will be running. You then just need to add a configuration to have PhpStorm execute the script.
Note: I am currently working on an IntelliJ PhantomJS integration plugin to make this easier.
Optional: Built-in server
As stated, I run my tests using PHP's built-in server instead of booting up my Docker containers. In fact, this is how our tests run on TravisCI. Create the runner and configure the domain and port.
Optional: Compound runner
I like to be
lazy efficient. We just created three different items that need to be running to execute the test suite. PhpStorm has a way to group runners, so they execute at once. I recommend making Compound runner that executes all three of the runners.