Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using PHPUnit to test helper functions

Tags:

php

phpunit

Let's say I want to test a simple helper that takes a class name as an argument and makes a redirection.

How am I supposed to test this if the function is called in many places from inside a couple of controllers? Should I test every class name that was passed as a parameter in the whole code (write them in the provider function myself)? Or is there a magical function which does that for me?

like image 599
user1615069 Avatar asked Aug 23 '12 17:08

user1615069


1 Answers

Your question is the exact reason why dependency injection -- when done correctly (not how most popular frameworks "implement" it) -- is touted as the ultimate in code testability.

To understand why, lets look at how "helper functions" and class-oriented programming make your controllers difficult to test.

class Helpers {
    public static function myHelper() {
        return 42;
    }
}

class MyController {
    public function doSomething() {
        return Helpers::myHelper() + 100;
    }
}

The entire point of unit testing is to verify that "units" of code work in isolation. If you can't isolate functionality, your tests are meaningless because their results could be tainted by the behavior of the other code involved. This can result in what statisticians call Type I and Type II errors: basically, this means you can get test results that might be lying to you.

In the code above, the helper cannot be easily mocked to determine that MyController::doSomething works in complete isolation from outside influences. Why not? Because we can't "mock" the behavior of the helper method to guarantee our doSomething method actually adds 100 to the helper result. We're stuck with the helper's exact behavior (returning 42). This is a problem that correct object-orientation and inversion of control eliminate entirely. Let's consider an example of how:

If MyController asks for it's dependencies instead of using the static helper function , it becomes trivial to mock the outside influences. Consider:

interface AnswerMachine {
    public function getAnswer();
}

class UltimateAnswerer implements AnswerMachine {
    public function getAnswer() {
        return 42;
    }
}

class MyController {
    private $answerer;
    public function __construct(AnswerMachine $answerer) {
        $this->answerer = $answerer;
    }
    public function doSomething() {
        return $this->answerer->getAnswer() + 100;
    }

}

Now, it's trivially simple to test that MyController::doSomething does in fact add 100 to whatever it gets back from the answer machine:

// test file

class StubAnswerer implements AnswerMachine {
    public function getAnswer() {
        return 50;
    }
}

$stubAnswer = new StubAnswerer();
$testController = new MyController($stubAnswerer);
assert($testController->doSomething() === 150);

This example also demonstrates how the correct use of interfaces in your code can greatly simplify the testing process. Test frameworks like PHPUnit make it very easy to mock interface definitions to perform exactly what you want them to in order to test the isolated functionality of code units.

So I hope these very simple examples demonstrate how powerful dependency injection is when it comes to testing your code. But more importantly, I hope they demonstrate why you should be wary if your framework of choice is using static (just another name for global), singletons, and helper functions.

like image 86
rdlowrey Avatar answered Sep 23 '22 13:09

rdlowrey