PHPUnit: Remove non-deterministic dependencies

Unit testing is great! But sometimes there are situations where it can become really tough to write proper tests for your code. One of these situations is when your code doesn’t work totally predictable, when it has some kind of randomness in it, that is intended. Then you usually have one of those functions in your code:

  • tempnam(), uniqid()
  • rand(), mt_rand(), shuffle() or any other randomized function
  • time(), date(), new \DateTime() or anything that has to do with the current time

The list isn’t complete, just wanted to mention the most common ones. Those functions are bad for testing, because tests have to be repeatable and those ones will most likely produce a different result on every call.

So how to get rid of the randomness and create predictable and therefore repeatable unit test?

The approach

The PHP community already has some solutions for that problem. In his blog Fabian Schmengler describes a solution that uses the PHP 5.3+ namespace model to overwrite native PHP functions. I personally think this is a quite clever solution, but I don’t like the way how PHP namespaces are exploited. For someone who isn’t used to this technique, I guess it can be really hard to understand. In my optinion unit tests should be as clear as possible, because they’re also documentation of your code in some kind of way.

A more clean alternative is this PHPUnit extension which enables you to mock PHP’s core functions, but it requires the PECL extension runkit in order to work. I think this is the way mocking PHP core functions should work, but I don’t like the fact that you have to extend PHPUnit and you need a PHP environment with a non-common extension.

What I’d like to have is something simple, which doesn’t require any evil hacks and works with a standard PHP and PHPUnit distribution. So here are the two solutions that I perfer most. Both of them make use of encapsulation to create something that Roy Osherove would call a seam:

“Seams are places in your code where you can plug in different functionality, such as stub classes.”

(Taken from The Art of Unit Testing by Roy Osherove)

The example

Let’s say we’re creating a blog where we want to display a random blog post. The implementation might look something like this:

namespace Original;

class BlogManager
{
    /**
     * @var \Original\BlogRepositoryInterface
     */
    private $repository;

    /**
     * @param \Original\BlogRepositoryInterface $repository
     */
    public function __construct(BlogRepositoryInterface $repository)
    {
        $this->repository = $repository;
    }

    /**
     * Get a random blog post
     *
     * @return \Original\BlogPost
     */
    public function getRandomPost()
    {
        $allPosts = $this->repository->getBlogPosts();
        $randomIndex = mt_rand(0, count($allPosts) - 1);
        return $allPosts[$randomIndex];
    }
}

The problem here is the mt_rand() function, because you cannot predict which blog post will be choosen.

Solution 1: Encapsulate in a class

In this first solution the non-deterministic part of the code is extracted and moved to a separated class RandomIndexGenerator. As a result the constructor needs to be extended to inject an instance of the index generator.

namespace Solution1;

use Original\BlogRepositoryInterface;

class BlogManager
{
    /**
     * @var \Original\BlogRepositoryInterface
     */
    private $repository;

    /**
     * @var \Solution1\IndexGeneratorInterface
     */
    private $indexGenerator;

    /**
     * @param \Original\BlogRepositoryInterface $repository
     * @param \Solution1\IndexGeneratorInterface $indexGenerator
     */
    public function __construct(BlogRepositoryInterface $repository, IndexGeneratorInterface $indexGenerator)
    {
        $this->repository = $repository;
        $this->indexGenerator = $indexGenerator;
    }

    /**
     * Get a random blog post
     *
     * @return \Original\BlogPost
     */
    public function getRandomPost()
    {
        $allPosts = $this->repository->getBlogPosts();
        $randomIndex = $this->indexGenerator->getRandomIndex(count($allPosts) - 1);
        return $allPosts[$randomIndex];
    }
}

interface IndexGeneratorInterface
{
    /**
     * Get random post number
     *
     * @param integer $max
     * @return integer
     */
    public function getRandomIndex($max);
}

class IndexGenerator implements IndexGeneratorInterface
{
    /**
     * Get random post number
     *
     * @param integer $max
     * @return integer
     */
    public function getRandomIndex($max)
    {
        return mt_rand(0, $max);
    }
}

For testing we can now implement an alternative class, which returns the exact value, that we need for our test case.

namespace Tests\Solution1;

use Solution1\IndexGeneratorInterface;

class IndexGeneratorStub implements IndexGeneratorInterface
{
    /**
     * @var integer
     */
    public $randomIndex;

    /**
     * Get random post number
     *
     * @param integer $max
     * @return integer
     */
    public function getRandomIndex($max)
    {
        return $this->randomIndex;
    }
}

In the test case it might look like this. Instead of creating a real stub class, you could also use PHPUnit’s mocking framework and replace the IndexGeneratorStub with some mock object.

$indexGenerator = new IndexGeneratorStub();
$indexGenerator->randomIndex = 1;
$blogManager = new BlogManager($repository, $indexGenerator);

+ Clean separation into a replaceable class
– Much code need (interface, accual class, stub class)
– Extra dependency in the constructor needed, just to make it testable

Solution 2: Encapsulate in a method

There’s an alternative way how to deal with it. You could also create a method in the same class, which contains only the non-deterministic part of the code. When testing, this method will be utilized to inject some deterministic and testable logic.

namespace Solution2;

use Original\BlogRepositoryInterface;

class BlogManager
{
    /**
     * @var \Original\BlogRepositoryInterface
     */
    private $repository;

    /**
     * @param \Original\BlogRepositoryInterface $repository
     */
    public function __construct(BlogRepositoryInterface $repository)
    {
        $this->repository = $repository;
    }

    /**
     * Get a random blog post
     *
     * @return \Original\BlogPost
     */
    public function getRandomPost()
    {
        $allPosts = $this->repository->getBlogPosts();
        $randomIndex = $this->getRandomIndex(count($allPosts) - 1);
        return $allPosts[$randomIndex];
    }

    /**
     * Get random post number
     *
     * @param integer $max
     * @return integer
     */
    protected function getRandomIndex($max)
    {
        return mt_rand(0, $max);
    }
}

Note that the method getRandomIndex() must be protected, which enables us to extend the class and overwrite the logic. For example the TestableBlogManager, which is used in unit tests instead of the original BlogManager class, could look like this:

namespace Tests\Solution2;

use Solution2\BlogManager;

class TestableBlogManager extends BlogManager
{
    /**
     * @var integer
     */
    public $randomIndex;

    /**
     * Get random post number
     *
     * @param integer $max
     * @return integer
     */
    protected function getRandomIndex($max)
    {
        return $this->randomIndex;
    }
}

Now we can set the exact index, that needs to be returned for the current test case.

$blogManager = new TestableBlogManager($repository);
$blogManager->randomIndex = 1;

+ It’s simple: less code needed, no extra dependency in constructor
– Less clean, more “hacky” solution

Conclusion

I personally tend to use the second solution when the code fragment is dead simple, typically one-liners like random functions, getting the current time() or creating a DateTime object. When it becomes more complicated I prefer the first solution, because it has better separation and it is a cleaner style of working.

Both solutions have the drawback, that there is at least some code, which is not covered by tests. In the best case it is just a single line of code, something that won’t go wrong with almost absolute certainty. To polish the code coverage report a little bit, you might want to use PHPUnit’s @codeCoverageIgnore annotation, to exclude the parts in your code that cannot be tested.

If you want to have a deeper look, the complete code can be found on my Github. Maybe you know some other solutions? Then I’d be glad if you’d let me know 🙂

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.