Unit Tests are not enough

For the last half a year I have been refactoring the next version of PHPixie ORM and writing unit tests for it. My goal is to bring it to 100% coverage ( right now it’s at 97% ). But as others have already stated, 100% coverage doesn’t really mean there are no bugs in the code, all it means is that the components are behaving in the way you intended them to.

One huge problem with unit testing is that it may not detect wrong parameters in method calls. For example take a look at this method:

//Checks whether string $a contains string $b
public function contains($a, $b) {
    ....
}

Let’s say we have it successfully unit tested and continue to a different method that relies on contains():

//Checks whether string $string contains 'cat'
public function containsCat($string) {
    return $this->stringTools->contains($string, 'cat');
}

Now we unit test the containsCat() by mocking the contains() call. Our unit tests pass and all is great.

A week after that someone decides to modify contains($a, $b) by reordering the arguments. So instead of checking whether $a contains $b it will now check whether $b contains $a. He then fixes the tests for that method and it seems everything is ok. Except that now our containsCat() method is broken, since it passes arguments in the wrong order. Out unit test will not tell us that because the call to contains() has been mocked.

This issue is somewhat mitigated by using type hinting, at least then if you reorder parameters of different types you may get an error stating that. This is why I really want PHP 7 to get static type hints, but even then, as with the contains() example, you still are not safe.

That is why you also need integration and functional tests where you can check the whole system or a set of components working together. These tests are usually much easier to write then unit tests, since they require using actual dependencies and only minimal mocking. They also help you save more time, as unlike unit tests they rarely need to change after code refactoring.

Actually I came to a conclusion that you should start with having functional tests first and only then drilling down to writing unit tests. And perhaps if you manage to cover over 80% of your codebase with functional tests you may find it fitting to skip unit tests altogether in some cases. This is especially true for websites where having behavioral tests ( like Behat ) not only provides you with means of testing the actual pages rendered, but also acts as a spec for the entire system.

2 comments

    1. Line coverage. I don’t think we have any tools to generate logical branch coverage in PHP. Although I usually try to cover all logical branches when I write tests

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>