-
Notifications
You must be signed in to change notification settings - Fork 7
Testing
In order to satisfy the users’ needs and requirements, we need a well and easy functioning software product. Software testing however can become time-consuming and painful. The distributed development and numerous software dependencies within our project are complicating matters further. This is why we introduced Travis CI, a free Continuous Integration (CI) platform. Together with PHPUnit, Composer and WP_Mock, we have a software bundle that eases integration and testing a lot.
As the name already suggests, the Travis CI platform offers a CI service. We use it together with our version control system Github. Once you have pushed to Github, it automatically triggers Travis CI to build and test the new version of our project. This includes Travis CI doing the following steps:
- Assign VM
- Clone repository
- Execute
.travis.ymlfile - Execute PHPUnit tests
- Return result
Generally speaking, CI is a software development practice where members of a team integrate their work frequently. Software integration refers to the practice of combining individually tested software components into an integrated whole. Each integration will then be verified by building the project and testing the result.
How the project itself should be built is specified within the .travis.yml file, within our cms-directory. This, among other things, includes the specification of the programming language in use, dependency installation, as well as webserver (Apache) and database (MySQL) setup. If not stated differently, Travis CI will assume PHPUnit as the testing framework of choice, which is indeed the case for our project.
Travis CI provides a web interface, where you can inspect the steps taken in order to build and test your git commit.
Example:
Viewing the website, you’ll see a log of what has happened:
-
worker informationandbuild system informationprovides details about the system and its pre-installed software. -
git clone --depth=50 https://github.com/Integreat/cms.git Integreat/cms, our git repository is cloned - Next, PHP version 5.5 is installed
- Line 178 to 627 are the execution results of the commands given by our
.travis.yml
The majority of the log is self-explaining. However, the Composer part isn’t and this is why we dive a bit deeper here.
This section is not necessarily required for testing. However, reading it provides a better overall understanding of the subject. As your PHP applications become larger and more complex, managing dependencies via a list of includes becomes a chore, and it's also seen as bad practice today. The Composer manages packages on a per-project basis, installing them inside of a project. Thus, it is a dependency manager.
Example:
- You have a project that depends on a number of libraries.
- Some of those libraries depend on other libraries.
- Composer enables you to declare the libraries you depend on.
- Composer finds out which versions of which packages can and need to be installed, and installs them (including the packages your required package depend on)
To start using Composer in a project, all we need is a composer.json file, like the one we have.
{
"require-dev": {
"10up/wp_mock": "dev-master",
"phpunit/phpunit": "4.8.*",
"mockery/mockery": "^0.9.4",
"guzzlehttp/guzzle": "^6.1"
},
"require": {
"wp-cli/wp-cli": "^0.22.0"
}
}
This file describes dependencies of a project. The first (and often only) term specified within the composer.json is the require key, telling Composer which packages the project depends on. The require-dev packages are packages that aren't necessary for our project to work and that shouldn't be included in the production version of it (like PHPUnit).
By running the command composer install (see Travis CI log, line 178) all the dependencies are downloaded from the Packagist repository and installed locally (which you can view within the log file, too, when expanding line 178) within the vendor directory. The downloaded packages very often utilize additional packages, too. The WP-CLI application for instance requires various Symphony packages (see the composer.json of wp-cli within the vendor/wp-cli/wp-cli/ directory). Composer of course takes care of those dependencies by downloading them for us.
Finally, we have all required packages and their dependencies installed within the vendor directory. But how can we use them?
Without Composer, we would still have to include/require all the classes of the projects needed for our project. Composer however provides a feature called Autoloading. Basically, all we have to do – in order to be able to use all the classes right away – is to include one tiny piece of code within some central bootstrap file:
require_once __DIR__ . '/../vendor/autoload.php';
This is just a fake loader for vendor/composer/autoload_real.php, which in turn generates a couple of autoload files within the vendor/composer directory. As a result, an autoload_classmap.php file is generated, which contains a key-value map. With the key being the name abstraction of the class, the value represents the absolute path of that class.
Example entry:
'File_Iterator' => $vendorDir.'/phpunit/php-file-iterator/src/Iterator.php'
Now we can easily instantiate the Iterator-class, within our code, without the need for further includes:
$iterator = new File_Iterator();
In the background, Composer now scans autoload_classmap.php for the key File_iterator and automatically includes 'path/to/vendor/vendor/phpunit/php-file-iterator/src/Iterator.php' one line above, internally using PHP’s spl_autoload_register() function.
You have pulled and installed the Integreat project as described, and thus you should have PHPUnit installed already. Composer did everything for you. PHPUnit has a central configuration file, phpunit.xml. As you have pulled this file from github/Integreat, it comes preconfigured and should look like this:
<phpunit bootstrap="tests/bootstrap.php">
<testsuites>
<testsuite name="Unit">
<directory>tests/unit</directory>
</testsuite>
<testsuite name="Integration">
<directory>tests/integration</directory>
</testsuite>
</testsuites>
</phpunit>
The XML-file specifies the bootstrap file (to launch vendor/autoload.php) and the location for test cases. As you can see, we have two directories for test cases. One is for unit-tests and the other is for integration tests.
As for the directory structure within the tests/unit/ folder, we suggest that it should match the codebase directory structure. Example:
Your root directory (wordpress) looks somewhat like this:
./wordpress/.git/
… ./wordpress/tests/ ./wordpress/wp-admin/` ./wordpress/wp-content/ …
Now imagine, you want to test your new plugin ig-my-new-plugin (this is by the way our naming convention for self-written plugins: 'ig' prefix and dashes in between), which is located at
./wordpress/wp-content/plugins/ig-my-new-plugin/my_new_plugin.php
The corresponding test case should be located at:
./wordpress/tests/unit/wp-content/plugins/ig-my-new-plugin/my_new_pluginTest.php
As for the naming convention for unit tests, we suggest 'filenameTest'.
Now that we finally decided on where to store the file and how its name should be, it’s time to write a first test. We want to write a very simple unit test for our new plugin.
For the test case, we need to create a class a name equal to the filename. Furthermore, that class has to extend the PHPUnit_Framework_TestCase.
class my-new-pluginTest extends PHPUnit_Framework_TestCase{
public function testTrueIsTrue() {
$this->assertTrue(true);
}
}
Within the test case, we only have one statement:
$this->assertTrue(true);
which is semantically equal to:
if (true == true) return test passed
else return test failed
Similarly, there’s an assertFalse() function. PHPUnit provides a whole bunch of different assert functions. assertArrayHasKey(), assertEquals(), assertFalse(), assertSame() and assertTrue() are the most common ones.
For further information I recommend the tutorial of Juan Treminio or the official documentation.
But before we leave this chapter, I would like to address the concept of Mocking. Modelling the expected behaviour is easy if the code you want to test is transparent, meaning that it is comprehensible, not too extensive, independent from inaccessible 3rd party code, …
Imagine, within your code you utilize a 3rd party web API. Testing your code, everything could happen. Your request could be invalid or the servers providing the API could be temporarily inaccessible. What if the interface changes? All those cases makes test fail, although it’s actually not your code that’s wrong.
Mocking (Mock: Ger. “Attrappe”) is a technique, used in software testing to get rid of the problems stated above. You basically replace those complicated, opaque parts of the code with simple fake code, modelling the expected behaviour. For further information, I recommend the tutorial of Juan Treminio or the documentation of the PHPUnit mocking framework.
There are various mocking Frameworks that ease the use of mocks a lot. PHPunit comes up with one as well as Wordpress does. The mocking Framework of Wordpress is called WP_Mock and it is utilized to mock Wordpress behaviour. A good starting point here is provided by the Github site of the WP_Mock project.
The composer.json defines dependencies on phpunit for the actual unit tests, wp_mock for mocking WordPress functionality and mockery to mock classes (e.g. from other plugins).
There is also a dependency on guzzle to send HTTP requests.
Every test class should then extend TestCase from WP_Mock\Tools\TestCase so that the testing framework is properly set up.
Note that unit tests as of now only work for plugins that do not have any dependencies on the WordPress core or other plugins since these are only partially or not at all loaded.
At test case could then look like this:
<?php
use WP_Mock\Tools\TestCase;
class DummyTest extends TestCase {
public function testTrueIsTrue() {
$this->assertTrue(true);
}
}
To run the tests, run phpunit in your WordPress directory.