在phpspec中测试多种方法的调用

时间:2015-12-26 18:37:35

标签: php testing bdd phpspec

在过去,我总是偶然发现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();
}

这里我验证请求是否为特定端点提供了所有必要的信息(或抛出异常),然后处理请求。我选择此模式将验证与端点逻辑分开。

1 个答案:

答案 0 :(得分:1)

<ProphecyPhpSpec使用的模拟框架,非常自以为是。它遵循模仿者的方法(伦敦TDD学院),它捍卫我们应该一次描述一种行为。

模拟是一种测试,因此您希望每次测试都保留一个模拟。你可以模拟所有的电话,但这看起来不会很优雅。建议的方法是分离您正在测试的行为,并选择该行为所需的模拟,并对其余调用进行存根。如果您发现自己在一个测试中创建了大量存根,表明功能令人羡慕 - 您应该考虑将行为移动到被调用者,或者在中间添加一个人。

假设您决定继续并描述您拥有的代码,而无需重构。如果您对第二个电话感兴趣,根据您的示例,您应该使用textfield或类似电话来存根其他电话。例如。 willReturn代替$endpoint->setRequest(Argument::type(Request::class))->willReturn()