使用PHPUnit测试辅助函数

时间:2012-08-23 17:49:23

标签: php phpunit

假设我想测试一个简单的帮助器,它将类名作为参数并进行重定向。

如果在几个控制器内部的许多地方调用该函数,我该如何测试?我应该测试在整个代码中作为参数传递的每个类名(自己在提供者函数中写入)吗?或者是否有一种神奇的功能可以帮助我?

2 个答案:

答案 0 :(得分:13)

你的问题是依赖注入的确切原因 - 当正确完成(而不是最流行的框架如何“实现”它) - 被吹捧为最终的代码可测试性。

要理解原因,让我们看看“辅助函数”和面向类的编程如何使您的控制器难以测试。

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

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

单元测试的全部要点是验证代码的“单元”是否孤立运行。如果您无法隔离功能,那么您的测试就毫无意义,因为它们的结果可能会被其他相关代码的行为所污染。这可能导致统计学家称之为I型和II型错误:基本上,这意味着您可以获得可能对您说谎的测试结果。

在上面的代码中,无法轻易模拟帮助程序以确定MyController::doSomething与外部影响完全隔离 。为什么不?因为我们不能“模拟”辅助方法的行为来保证我们的doSomething方法实际上为辅助结果添加了100。我们坚持帮助者的确切行为(返回42)。这是一个正确的面向对象和控制反转完全消除的问题。让我们考虑一个如何的例子:

如果MyController 询问它的依赖关系而不是使用静态辅助函数,那么模拟外部影响就变得微不足道了。考虑:

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;
    }

}

现在,测试MyController::doSomething实际上为从答案机器返回的内容增加100来简单易行:

// test file

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

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

此示例还演示了如何在代码中正确使用接口可以极大地简化测试过程。像PHPUnit这样的测试框架使模拟接口定义变得非常容易,以便完全按照您的意愿执行,以便测试代码单元的隔离功能。

所以我希望这些非常简单的例子展示了在测试代码时依赖注入的强大功能。但更重要的是,我希望他们能证明为什么你应该谨慎,如果你选择的框架是使用静态(只是全局的另一个名称),单例和辅助函数。

答案 1 :(得分:0)

您无法测试每个可能的参数组合到您需要测试的所有功能;它会比宇宙的生命花费更长的时间。所以你使用人类智能(有些人可能称之为作弊;-)。只测试一次,在这种情况下使用模拟控制器作为参数。

然后查看你的代码,并问自己是否传入任何其他对象确实会使其行为不同。对于你描述为“简单助手”的事情,答案可能是肯定的。但是,如果是的话,怎么样?创建另一个模拟不同行为的模拟控制器类。例如。第二个控制器可能没有你的助手类期望调用的函数。您希望抛出异常。为此创建单元测试。

重复直到满意为止。