我正在学习 phpunit 并注意到很多示例似乎是模拟一个类,然后将其注入另一个类,然后测试该方法。我想知道如果我想不注射就测试怎么办?
现在,我希望它失败,所以我知道它正在使用模拟返回值,但测试通过是因为它没有使用模拟值。我怎样才能使用模拟部分?谢谢。
这是一个简单的例子:
class Check
{
public function checkAdd($num1, $num2)
{
$sum = $num1 + $num2;
$calculator = new Calculator();
$res = $calculator->add($num1, $num2);
return ($sum == $res);
}
}
class Calculator
{
public function add($num1, $num2)
{
return $num1 + $num2;
}
}
use PHPUnit\Framework\TestCase;
require_once __DIR__ . '/../Check.php';
require_once __DIR__ . '/../Calculator.php';
class CheckTest extends TestCase
{
public function testValidateAdd()
{
$calculatorMock = $this->getMockBuilder(Calculator::class)->getMock();
$calculatorMock->method('add')->willReturn(3);
$check = new Check();
$result = $check->checkAdd(5, 2);
$this->assertTrue($result);
}
}
由于
public function checkAdd($num1, $num2)
正在使用 Calculator
的具体实现
public function checkAdd($num1, $num2)
{
$sum = $num1 + $num2;
$calculator = new Calculator(); // !! HERE !!
$res = $calculator->add($num1, $num2);
return ($sum == $res);
}
您将无法用测试替身(模拟、存根等)替换它。
为了使用测试替身,您必须以某种方式将其处理为 SUS(被测系统)。在你的例子中类似:
Check
创建构造函数并传递计算器checkAdd
函数(我不会这么做;尽管如此,我还是想分享)现在您知道为什么不起作用以及如何解决它,让我们分析您的代码。 由于
Calculator
没有副作用(它是一个纯函数)并且不是外部依赖项(如数据库、文件系统、网络、api 等),因此并不严格需要使用测试替身来替换它。请记住,每次使用测试替身时,您都会在测试维护方面付出一些额外的努力(就好像您很可能更改实现一样,您也需要更改测试代码)。
硬币的另一面,通过您的实现,您正在创建一个对象并在同一代码片段中使用它。这意味着您正在
Check
和 Calculator
之间创建耦合。这本身并不是一个糟糕的设计选择,只是想强调一下。
顺便说一句,有人可能会说,使用具体的实现会违反单元测试的基本原则,因为人们往往对单元是什么以及孤立测试的含义感到困惑。用几句话来解释一下:单元只是您正在测试的代码,因此可以是一个方法、整个类,甚至可能是多个对象的组合(也许令人惊讶)。隔离意味着您可以以不同的顺序运行测试,也许可以并行运行,而不会影响另一个。