在过去,我总是偶然发现phpspec的某个问题:
假设我有一个方法可以在另一个对象上调用多个方法
class Caller {
public function call(){
$this->receiver->method1();
...
$this->receiver->method2();
}
}
在BDD中,我会首先编写一个测试,确保调用 method1 。
function it_calls_method1_of_receiver(Receiver $receiver){
$receiver->method1()->shouldBeCalled();
$this->call();
}
然后我会编写下一个测试以确保将调用 method2 。
function it_calls_method2_of_receiver(Receiver $receiver){
$receiver->method2()->shouldBeCalled();
$this->call();
}
但是这个测试在phpspec中失败了,因为在 method2 之前调用 method1 。 为了满足phpspec,我必须检查两个方法调用。
function it_calls_method2_of_receiver(Receiver $receiver){
$receiver->method1()->shouldBeCalled();
$receiver->method2()->shouldBeCalled();
$this->call();
}
我的问题是,每次测试都会膨胀。在这个例子中,它只是一个额外的行,但想象一个用很多setter构建一个对象的方法。 我需要为每个测试编写所有的setter。由于每次测试都很大并且外观相同,因此很难看到测试的目的。
我非常确定这不是phpspec或bdd的问题,而是我的架构问题。写这个会更好(更可测试)的方法是什么?
例如:
public function handleRequest($request, $endpoint){
$endpoint->setRequest($request);
$endpoint->validate();
$endpoint->handle();
}
这里我验证请求是否为特定端点提供了所有必要的信息(或抛出异常),然后处理请求。我选择此模式将验证与端点逻辑分开。
答案 0 :(得分:1)
模拟是一种测试,因此您希望每次测试都保留一个模拟。你可以模拟所有的电话,但这看起来不会很优雅。建议的方法是分离您正在测试的行为,并选择该行为所需的模拟,并对其余调用进行存根。如果您发现自己在一个测试中创建了大量存根,表明功能令人羡慕 - 您应该考虑将行为移动到被调用者,或者在中间添加一个人。
假设您决定继续并描述您拥有的代码,而无需重构。如果您对第二个电话感兴趣,根据您的示例,您应该使用textfield
或类似电话来存根其他电话。例如。 willReturn
代替$endpoint->setRequest(Argument::type(Request::class))->willReturn()
。