Unit Testing IV - A Practical Test

A unit test that actually tests something

In the previous article in this series, we created a very simple unit test to make sure the testing platform was working. In this one we'll extend that test to do some actual testing on a User class.

This article assumes that you have successfully installed Codeception (which installs PhpUnit) with Composer and created the unit test described in the previous articles.

MODX logo

A Real Test

In the build directory, give this command:

codecept generate:test unit T1_UserConstructor

Now copy the code below into the new T1_UserConstructorTest.php file.

Our first test (in the previous article) just showed that the unit test platform was working. In this article, we'll create a minimal User class called User1.class.php (but not yet). First, we'll create some tests we want the class to pass. Let's add a test of our class's constructor. We want the constructor to create a User1 object with empty fields when called with no parameters (arguments) and a User1 object with fields filled from the parameters. Here is the new test file code for the T1_UserConstructorTest.php file (the new code is below the _before() and _after() methods):


class T1_UserConstructorTest extends \Codeception\Test\Unit {
     * @var \UnitTester
    protected $tester;

    protected function _before() {
        require_once 'c:/xampp/htdocs/addons/assets/mycomponents/testingproject/core/model/user1.class.php';

    protected function _after() {

    /* Tests */
    public function testWorking() {

    public function testConstructorWithParams() {
        $user = new User1(array(
            'username' => 'BobRay',
            'email' => 'bobray@hotmail.com',
            'phone' => '651-423-1548'
        assertInstanceOf('User1', $user);
        assertEquals('BobRay', $user->get('username'));
        assertEquals('bobray@hotmail.com', $user->get('email'));
        assertEquals('651-423-1548', $user->get('phone'));

    public function testConstructorNoParams() {
        $user = new User1();
        assertInstanceOf('User1', $user);
        assertEquals('', $user->get('username'));
        assertEquals('', $user->get('email'));
        assertEquals('', $user->get('phone'));

Save the file and run the test again (most terminals will let you repeat that last command if you press the up arrow). The test code is available from GitHub here.

codecept run unit T1_UserConstructorTest

You should see this:

[Error] Class 'User1' not found

That makes sense because we haven't created our User1 class yet.

The User Class File

Create a user1.class.php file somewhere. Typically it will be outside the _build directory. You could, for example, create a classes directory at the same level as the _build directory and put the file there. In my example (and at the GitHub repository), it's in the core/model/ directory.

Use this code in the user1.class.php file:

    class User {
        protected $fields = array(
            'username' => '',
            'email' => '',
            'phone' => ''

    /* Note that there are two underscores before the word "construct" */
    function __construct($properties = array()){
        if (!empty($properties)) {
            foreach ($properties as $key => $value) {
                if (array_key_exists($key, $this->fields )) {
                    $this->set($key, $value);

    public function set($fieldName, $value) {
        $this->fields[$fieldName] = $value;
    public function get($fieldName) {
        if (array_key_exists($fieldName, $this->fields)) {
            return $this->fields[$fieldName];
        } else {
            return '';

    public function save() {
        return true;

The code above is available at my GitHub repository, "TestingProject" at this url.

Save the class file, then add this code to the _before() function (correct the path) in the test file we created at the beginning of this article:

require_once 'path/to/user/file/user1.class.php';

Run the test again.

codecept run unit T1_UserConstructorTest

You should see this:

OK (3 tests, 11 assertions)

As we said earlier, rather than adding the code to the _before() function, you could add it to a _bootstrap.php file. You'd add this to the unit.suite.yml file:

    bootstrap: _bootstrap.php

(or this for later versions of Codeception):

bootstrap: _bootstrap.php

Then you could add the require_once statement to the _build/tests/unit/_bootstrap.php file (creating it if necessary) and add the require_once line to that file. Since you're likely to have other tests that don't need the User1 class file, it makes more sense to put the line in the _before() method.

You should already have the _build/tests/unit/_bootstrap.php file, if you've been following this series. If it's there, verify that it contains an opening PHP tag and the following lines (correct the paths). If not, create it and add the PHP tag and these lines:

require_once 'c:/xampp/htdocs/addons/vendor/autoload.php';
require_once 'c:/xampp/htdocs/addons/vendor/phpunit/phpunit/src/Framework/Assert/Functions.php';

These includes will be necessary for all our tests, so it's more convenient to load them in the _bootstrap.php file rather than having to remember to include them in the _before() method of every test.

Running Tests from the Command Line

Use the command-line method for running tests. It should run reliably for our early examples with no configuration necessary beyond the default files generated by Codeception. Later, you can try running the tests from inside your code editor if it supports that feature. Some people run all their tests from the command line.

Important: To run any Codeception tests from the command line without having to specify the location of the configuration file, you must be in the directory directly above the tests directory (_build in our example). To run or debug tests inside PhpStorm, you must specify that _build directory (with no final slash) as the "Custom working directory" in the Run/Debug configuration (more on this in the following article).

As we saw in the previous article, from the command line, while in the directory above the tests directory (the _build directory in our example), you can run run all test files in all suites, all test files in a particular suite, specific test files (codecept run unit UserTest), or a single method (function) in one of the test files.

Run Commands

We covered this in the last article, but here's a review of the Codeception run command. It's assumed that you are in the _build directory for all these commands:

  • Run all test files in all suites: codecept run
  • Run all test files in a single suite: codecept run unit
  • Run a single test file in a suite: codecept run unit T1_UserConstructorTest
  • Run all test files in two suites: codecept run unit,integration
  • Run a single method/function in one test file: codecept run unit T1_UserTest:testConstructor

Codeception runs the last command above as if it has a wildcard at the end of the method name. So if two of the methods in your UserTest file are called testConstructorWithParams() and testConstructorNoParams(), the last command above will run both of them. codecept run unit UserTest:testCon would also work.

The commands above assume that you are using Codeception. If you are using PhpUnit alone, you'll have to change the extends line at the beginning of your test file to extend the PhpUnit class:

class MyClass extends PHPUnit\Framework\TestCase {}

You'll also have to use phpUnit rather than codecept in your commands. Even after doing that, some run commands may not work. For example, the command to run a single method of a test file looks like this for PhpUnit:

phpunit --filter methodName path/to/file.php

Coming Up

We'll get back to enhancing and testing our User class, but in the next article, we'll see how to run Codeception tests in PhpStorm.

For more information on how to use MODX to create a web site, see my web site Bob's Guides, or better yet, buy my book: MODX: The Official Guide.

Looking for high-quality, MODX-friendly hosting? As of May 2016, Bob's Guides is hosted at A2 hosting. (More information in the box below.)

Comments (0)

Please login to comment.