Integration Testing IV - Codeception Helper Methods

Using Codeception helper methods


In the previous article in this series, we looked at integration testing for code that runs inside the MODX CMS using Codeception's Db module. In this one we'll look at helper methods in Codeception.


MODX logo

This article assumes that you've installed and configured Codeception, PhpUnit, and MODX as described in the previous articles. The test files and classes are available at GitHub here.


Helpers

In the previous article, we used a simple utility function _removeUsers() to make sure our test users were not in the database. This method is fine if you have a few simple utility functions you need for a test. If you have a lot of them, or if they are complex, you might want to move them out of the class and make them "Helper" functions in Codeception.

Helper functions in Codeception are modules. That means that, like the Db module we saw in the last article, they are available as methods of the actor used in a test. If your actor is $tester and removeUsers() is set up as a Helper, you can call it with $tester->removeUsers($modx, $userNames).

Inside a test class file, utility functions like our _removeUsers() method are prefixed with an underscore to identify them as non tests. When they are Helpers, however, the prefix should be removed.

In a Helper class, methods that start with an underscore will not be added to the Actor class and will only be available inside the Helper method. In other words they are utility functions used by the actual helper methods, but not available as Helper methods themselves.


Making removeUsers() a Helper


Creating the Helper

You should already have a file called Integration.php in the _build/tests/_support/Helper directory. If not, issue this command in the _build directory:

codecept generate:helper Integration

Open that Integration.php file in your editor. Take a look at the code. It should already have namespace Helper at the top. The shell is already there, we just need to add some helper methods. Replace everything that's there with this code:

<?php
namespace Helper;

use \modX;
use \modUser;

class Integration extends \Codeception\Module {

    /**
     * @param modX $modx
     * @param array $usernames
     */
    public function removeUsers($modx, $usernames) {
        /** @var modUser $user */
        foreach ($usernames as $username) {
            $user = $modx->getObject('modUser', array('username' => $username));
            if ($user) {
                $user->remove();
            }
        }
    }
}

In the code above, the namespace Helper is necessary to tell Codeception where to find the Helpers. The two use lines below it are not always necessary, but are good form. They identify the modX and modUser classes as part of the global namespace and not in the Helper namespace. Without them, a good code editor will complain about not finding their methods and won't autocomplete their methods.

Otherwise the removeUsers() code is exactly the same as it was when it was in the test file except for the missing underscore and the change to a Public function. Important: all Helpers must be declared as Public.


Updating the Config File

In order to use the helper method above, we need to complete two more steps. First, add these lines to the integration.suite.yml file if they're not already there:

    actor: IntegrationTester

    enabled:
        - \Helper\Integration
        # leave the Db code here

The line above tells Codeception where to look for the Integration test helper file.

Second, from the _build directory, issue this command:

    codecept build

Codeception will do its usual build steps, one of which is to add a removeUsers() method to the IntegrationTesterActions.php file found in the _build/tests/_support/_generated directory. It goes there because we identified our Integration test actor as IntegrationTester in the first line of the integration.suite.yml configuration file.

This generated method in the IntegrationTesterActions.php file is the one that's actually called when you call $this->tester->removeUsers() in the test file. It calls the "real" method in the Helper directory.


The T9_HelperTest.php File

We need to modify the test file to use the removeUsers() Helper. Create the new test file at the command line from the _build directory with this command:

codecept generate:test integration T9_Helper

Replace the content of that file with the code below (also available at GitHub here).

<?php
use Codeception\Util\Fixtures;


class T9_HelperTest extends \Codeception\Test\Unit
{
    /**  @var $tester IntegrationTester */
    protected $tester;

    /** @var modX $modx */
    public $modx;

    protected $usernames = array(
        'User1','User2',
    );

    protected function _before()
    {
        $this->modx = Fixtures::get('modx');
        assertTrue($this->modx instanceof modX);
        $this->tester->removeUsers($this->modx, $this->usernames);
    }

    protected function _after()
    {
        $this->tester->removeUsers($this->modx, $this->usernames);
    }

    // tests
    public function testWorking()
    {
        $I = $this->tester;
        assertTrue(true);
        $I->dontSeeInDatabase('modx_users', array('username' => 'User1'));
        $I->dontSeeInDatabase('modx_users', array('username' => 'User2'));
    }

    public function testSaveUser() {
        /** @var @var modUser $user */
        $I = $this->tester;
        $user = $this->modx->newObject('modUser');
        $user->set('username', 'User1');
        assertTrue($user->save());
        $I->seeInDatabase('modx_users', array('username' => 'User1'));
    }

    public function testUpdateUser() {
        $I = $this->tester;
        $I->dontSeeInDatabase('modx_users', array('username' => 'User1'));
        $I->dontSeeInDatabase('modx_users', array('username' => 'User2'));
        $user = $this->modx->newObject('modUser');
        $user->set('username', 'User1');
        assertTrue($user->save());
        $I->seeInDatabase('modx_users', array('username' => 'User1'));
        $user = $this->modx->getObject('modUser', array('username' => 'User1'));
        assertNotEmpty($user);
        assertInstanceOf('modUser', $user);

        $user->set('username', 'User2');
        assertTrue($user->save());
        $I->dontSeeInDatabase('modx_users', array('username' => 'User1'));
        $I->seeInDatabase('modx_users', array('username' => 'User2'));
    }

    public function testDeleteUser() {
        $I = $this->tester;
        $I->dontSeeInDatabase('modx_users', array('username' => 'User1'));
        $user = $this->modx->newObject('modUser');
        $user->set('username', 'User1');
        assertTrue($user->save());
        $I->seeInDatabase('modx_users', array('username' => 'User1'));
        $user->remove();
        $I->dontSeeInDatabase('modx_users', array('username' => 'User1'));
    }
}

Changes

The Changes to this file are minimal, all we've done is change the lines in the _before() and _after() methods to call the Helper:

$this->tester->removeUsers($this->modx, $this->usernames);

We've also removed the _removeUsers() method from the test file since it's no longer called.


Other Helpers

You're free to create as many helper methods as you need in the files under the _build/tests/_support/Helper directory. Be sure to run codecept build any time you make a change there, otherwise Codeception won't find your Helper method.

One common use of Helpers is to create new assertions. Codeception recommends that you prefix all Helper assertions with "see" or "dontSee" so they won't be confused with native PhpUnit assertions.

A while back we showed a complex assertion that checked to see if a number was in a certain range:

assertTrue($number <= 25 && $number <= 50);

This could be a Helper method:

public function seeIsInRange($number, $min, $max) {
    $this->assertTrue($number <= $min && $number <= $max, "Number is not in range");
}

In your test file, you'd call that with something like this:

$I->seeIsInRange($number, 25, 50);

Notice that the Helper doesn't return anything, it just makes its own complex assertion. It could make more complex calculations before using an assertion to test them.

There's more information about Codeception Modules and Helpers here.


Why Not Have a Helper to Get MODX?

If you remember our method in the _bootstrap.php file that instantiates MODX and adds it to the Fixtures array, you might wonder why we shouldn't create a Helper method that instantiates MODX and then returns it. The problem with this is that the process of instantiating MODX would happen every time we needed it. By making it a Fixture, this only happens once and the instance of MODX is available anywhere in your test code.


Coming Up

In the next article, we'll look at how to write integration test code for a processor class.



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.

  (Login)