PHPUnit tips

Hopefully, all this phpunit tips can help you to test your own applications. If you have any questions, or suggestions, feel free to contact me!Some people have asked me to write something about testing, so I’ve decided to make this post and share some phpunit tips that I’ve learn.

This is not meant to be another tutorial of how to start writing your own tests with phpunit. Well, actually it is, but the purpose of my post is to explain you how to deal with real scenarios. If you try to search for phpunit examples, most places will show you how to create very simple test cases, but I haven’t seen many places explaining how to do more complex things. That’s exactly the purpose of this article. Moreover, If you think something’s missing, I encourage you to ask me about it, and I’ll try to append it to the post, or if it’s a big enough subject, I’ll create a whole new article about it.

Let’s begin from a very simple case, and we’ll get into something a bit more complicated step by step.
I assume that you already know what’s PHPUnit, and some basic concepts of how to use it, like for instance the dataproviders or mocks. If you think that you need some help to get started, have a look at PHPUnit’s official page and feel free to ask me, if needed.

Happy case in PHPUnit: Imagine you have an isolated class and you wanna test some of its methods.


/**
 * This code has been taken just as an example, from: http://snipplr.com/view.php?codeview&id=9024
 */
 class TheDeveloperWorldIsYours {
public static function generateUrlFromText($strText) {
 $strText = preg_replace('/[^A-Za-z0-9-]/', ' ', $strText);
 $strText = preg_replace('/ +/', ' ', $strText);
 $strText = trim($strText);
 $strText = str_replace(' ', '-', $strText);
 $strText = preg_replace('/-+/', '-', $strText);
 $strText = strtolower($strText);
 return $strText;
}
}

How would you test that? This might be an attempt:

/**
 * TheDeveloperWorldIsYours_Test
 *
 * @uses PHPUnit_Framework_TestCase
 */
class TheDeveloperWorldIsYours_Test extends PHPUnit_Framework_TestCase {

    public function setUp() {
    }

    protected static $inputs = array(
        array('   string    with  many   spaces   ', 'string-with-many-spaces'),
        array('stríng wïth wêird Àccents', 'str-ng-w-th-w-ird-ccents'),
        array('$peci4l: ch%r·ct3r$.', 'peci4l-ch-r-ct3r'),
        array('nice-string', 'nice-string'),
        array('testing+with+phpunit-is-cool', 'testing-with-phpunit-is-cool')
    );

    public static function providerFormat()
    {
        return self::$inputs;
    }

    /**
     * testFormat
     * Ensure that the string is being properly cleaned
     *
     * @dataProvider providerFormat
     * @param mixed $input
     * @param mixed $result
     * @access public
     * @return void
     */
    public function testFormat($input, $result) {
        $this->assertEquals($result, TheDeveloperWorldIsYours::generateUrlFromText($input));
    }
}

I assume that you have already set up some boostrap that takes care of including the required files (in this case, we just need to include the file where the class TheDeveloperWorldIsYours before executing the test).
If you run the test file with PHPUnit, you’ll notice that all the tests are passing, and you might want to add some corner cases to feel more comfortable, such as very long strings, empty strings, numbers and so. However, that’s out of the scope of this post (I could create a whole article about it). Nobody would know better than you the casuistic of your application, and you are the one responsible to add all the missing tests in each case.

So, in this example, we have the best scenario: The method is public, and even static, so we don’t need to instantiate the class in order to test the method. Also, the class doesn’t inherits from any other class, so we don’t have to worry about including all the required files.

Now, let’s assume we had the following scenario:

 class TheDeveloperWorldIsYours extends Some_Parent_Class {
protected function generateUrlFromText($strText) {
 $strText = preg_replace('/[^A-Za-z0-9-]/', ' ', $strText);
 $strText = preg_replace('/ +/', ' ', $strText);
 $strText = trim($strText);
 $strText = str_replace(' ', '-', $strText);
 $strText = preg_replace('/-+/', '-', $strText);
 $strText = strtolower($strText);
 if($strText === '') {
    die('Unable to generate a valid url from the given input');
 }
 return $strText;
}
}


Okay, let’s have a look at the problems that we have here.
There is a spanish said: “divide y vencerás” (divide and conquer), so let’s do it:

  • Our goal is to test a class method that is inside a child of Some_Parent_Class, which means that we have to include the parent class in order to be able to test its children.
  • The method is not public nor static, which means that we cannot call it directly and therefoer we need to instantiate the class firstly.
  • We wouldn’t be able to test the case when $strText is empty, because the die statement would stop the PHP execution.

Solving the inheritance problem

Apart from the obvious, which would be including the parent’s file as well, couldn’t we make anything else? In this case, maybe it doesn’t worth to try it, but imagine a real application, where this class might be a child of a child of a child of a child.. and so. Do you really want to include all that files? Do you really need them in order to test this very simple method? In most cases the answer is no. Remember that here we are making a unit test, we only want to test this method, and we don’t care about the rest of the application, that’s not the purpose of the unitary tests.

So, what might we do, here? There is a pragmatic solution: Mock the parent class, with the minimum content/methods, in order to be able to instantiate the children class.

Testing a private/protected method

This is a so well-known problem and there are many workarounds if you google it. My favourite way, however, is using Reflection (Requires PHP 5.3.2+). However, if you are not using a supported version of PHP. You might just create a child class of TheDeveloperWorldIsYours, and create a method that calls the protected one and returns the result. Then you could instantiate this child class and run your tests against this method. That would only work for protected methods, of course, but it’s better than nothing.

Testing a die() or exit() call

This is a very interesting part, as most people doesn’t even try to test this cases. Basically, we want to know what happens if we call the method with an invalid input. The problem is that if that happens, the die function will stop the execution of the script, so we cannot ensure what’s really happening. If you try to test it, once the “die” line is executed, the PHP function stops, and so the phpunit execution does. After some research I found a way to test this cases, by installing test_helpers extension. Afterwards, you can easily overload/intercept the die/exit statements.

Here is how our test looks like, after applying the previously mentioned solutions:

/**
 * TheDeveloperWorldIsYours_Test
 *
 * @uses PHPUnit_Framework_TestCase
 */
class TheDeveloperWorldIsYours_Test extends PHPUnit_Framework_TestCase {

    private $testClass;
    private static $message = null;

    public function setUp() {
        //Mocking the parent class, in this case we don't need to mock any of its methods
        $this->getMock('Some_Parent_Class');
        require_once dirname(__FILE__) . '/thedeveloperworldisyours.php';

        //Allowing the method to be called from the outside by Reflection.
        $this->testMethod = new ReflectionMethod(
           'TheDeveloperWorldIsYours', 'generateUrlFromText'
        );
        $this->testMethod->setAccessible(TRUE);
    }

    protected static $inputs = array(
        array('   string    with  many   spaces   ', 'string-with-many-spaces'),
        array('stríng wïth wêird Àccents', 'str-ng-w-th-w-ird-ccents'),
        array('$peci4l: ch%r·ct3r$.', 'peci4l-ch-r-ct3r'),
        array('nice-string', 'nice-string'),
        array('testing+with+phpunit-is-cool', 'testing-with-phpunit-is-cool')
    );
    //We use this methods to be able to retrieve the die message, and ensure that everything went as expected.
    public static function setMessage($message) {
        self::$message = $message;
    }
    public function getMessage() {
        return self::$message;
    }

    public static function providerFormat()
    {
        return self::$inputs;
    }

    /**
     * testFormat
     * Ensure that the string is being properly cleaned
     *
     * @dataProvider providerFormat
     * @param mixed $input
     * @param mixed $result
     * @access public
     * @return void
     */
    public function testFormat($input, $result) {
        $this->assertEquals($result, $this->testMethod->invokeArgs(new TheDeveloperWorldIsYours, array($input)));
    }

    public function testInvalidInput() {
        //Now, every exit/die will execute this anonymous function
        set_exit_overload(function($message) { TheDeveloperWorldIsYours_Test::setMessage($message); return FALSE; });
        $this->testMethod->invokeArgs(new TheDeveloperWorldIsYours, array(''));
        //We remove the overload after testing the die call.
        unset_exit_overload();
        //Verify that the die statement was actually executed with the wrong input.
        $this->assertEquals('Unable to generate a valid url from the given input', $this->getMessage());
    }
}

Summarizing: Now we are able to test methods of classes that inherit from several parents without having to include any of them. Also, we know how to test private and protected methods. And the icing on the cake, we are able to test all the paths, including the die/exit statements.

Hopefully, all this phpunit tips can help you to test your own applications.

If you have any questions, or suggestions, feel free to contact me!

3 thoughts on “PHPUnit tips

  1. Pingback: Jenkins with PHP from scratch - I • The Developer World Is Yours

  2. “There is a pragmatic solution: Mock the parent class, with the minimum content/methods, in order to be able to instantiate the children class.”

    A problem I’ve run into with this is, if there is a unit test in the same suite for testing the parent class, then the stub parent class I’ve put into the unit test for instantiating the child class comes into conflict with the real parent class. The unit test suite crashes, indicating (correctly) that a class can’t be redeclared.

    Do you have a code example of how you would do this in the context of a larger test suite that also includes a test for the parent class?

    • Interesting question, right know I only can think of two approaches (there might be more and better):
      – One would be, including the parent class on the test that inherits from it, instead of mocking it (and if this one inherits from another one, mock it, unless you need to test it as well :)). That would introduce a dependency between the test of the child class and the test of the parent class. To avoid issues you could use class_exists, to conditionally include the parent class in the tests if it’s not already declared. This way you should be able to run both tests together and separately.

      – Another way, which is a bit of a hack but might work only for testing purposes would be playing with namespaces and eval (evil!). This is not recommended, but if for some reason you can’t follow the first approach you can give it a try. The idea is define the child class with the parent mock inside a namespace for one test, and then the other one can either run without namespace or inside a separated namespace, to avoid conflicts with other tests. Something like this:

      /*
      * We need eval to declare the class in the same namespace context, as it only affects the current file, hence including the child class underneath wouldn't work. To avoid changing anything on the target/tested class, include the php file content, removing the php tag (<?php) with substr, to be able to eval the code
      */
      eval('
      namespace test1;
      class Some_Parent_Class {}
      ' . substr(file_get_contents(__DIR__ . '/path/to/the/target/class.php'), 5)
      );
      

      Now you can test your child class that inherits from the parent mock by referring to it as test1/YourChildClass. Other tests wouldn’t complain about already declared class, as they would be running outside this namespace/scope.

      I hope it helps.

      Regards,
      Javier

Leave a Reply

Your email address will not be published. Required fields are marked *