在 phpspec 中测试多个方法的调用
Testing call of multiple methods in 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 中失败,因为 method1 在 method2 之前被调用。
为了满足 phpspec,我必须检查两个方法调用。
function it_calls_method2_of_receiver(Receiver $receiver){
$receiver->method1()->shouldBeCalled();
$receiver->method2()->shouldBeCalled();
$this->call();
}
我的问题是,每次测试都会膨胀。在此示例中,它只是一个额外的行,但可以想象一个方法可以构建一个具有大量 setter 的对象。
我需要为每个测试编写所有设置器。很难看出测试的目的,因为每个测试都很大而且看起来都一样。
我很确定这不是 phpspec 或 bdd 的问题,而是我的体系结构的问题。什么是更好(更可测试)的写法?
例如:
public function handleRequest($request, $endpoint){
$endpoint->setRequest($request);
$endpoint->validate();
$endpoint->handle();
}
在这里我验证请求是否为特定端点提供了所有必要的信息(或抛出异常)然后处理请求。我选择这种模式来将验证与端点逻辑分开。
Prophecy, the mocking framework used by PhpSpec,很有主见。它遵循 mockist 方法(TDD 伦敦学院),该方法捍卫我们应该一次描述一种行为。
模拟是一种测试,因此您希望每次测试都保留一个模拟。您可以模拟所有调用,但这看起来并不优雅。推荐的方法是将您正在测试的行为与 select 该行为所需的模拟分开,对其余调用进行存根。如果您发现自己在一个测试中创建了大量的存根,表明对功能的嫉妒——您应该考虑将行为转移到被调用者身上,或者在中间添加一个人。
假设您决定继续并描述您拥有的代码,而不进行重构。如果您对第二个调用感兴趣,按照您的示例,您应该使用 willReturn
或类似的方法对其他调用进行存根。例如。 $endpoint->setRequest(Argument::type(Request::class))->willReturn()
而不是 shouldBeCalled()
.
过去我总是偶然发现 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 中失败,因为 method1 在 method2 之前被调用。 为了满足 phpspec,我必须检查两个方法调用。
function it_calls_method2_of_receiver(Receiver $receiver){
$receiver->method1()->shouldBeCalled();
$receiver->method2()->shouldBeCalled();
$this->call();
}
我的问题是,每次测试都会膨胀。在此示例中,它只是一个额外的行,但可以想象一个方法可以构建一个具有大量 setter 的对象。 我需要为每个测试编写所有设置器。很难看出测试的目的,因为每个测试都很大而且看起来都一样。
我很确定这不是 phpspec 或 bdd 的问题,而是我的体系结构的问题。什么是更好(更可测试)的写法?
例如:
public function handleRequest($request, $endpoint){
$endpoint->setRequest($request);
$endpoint->validate();
$endpoint->handle();
}
在这里我验证请求是否为特定端点提供了所有必要的信息(或抛出异常)然后处理请求。我选择这种模式来将验证与端点逻辑分开。
Prophecy, the mocking framework used by PhpSpec,很有主见。它遵循 mockist 方法(TDD 伦敦学院),该方法捍卫我们应该一次描述一种行为。
模拟是一种测试,因此您希望每次测试都保留一个模拟。您可以模拟所有调用,但这看起来并不优雅。推荐的方法是将您正在测试的行为与 select 该行为所需的模拟分开,对其余调用进行存根。如果您发现自己在一个测试中创建了大量的存根,表明对功能的嫉妒——您应该考虑将行为转移到被调用者身上,或者在中间添加一个人。
假设您决定继续并描述您拥有的代码,而不进行重构。如果您对第二个调用感兴趣,按照您的示例,您应该使用 willReturn
或类似的方法对其他调用进行存根。例如。 $endpoint->setRequest(Argument::type(Request::class))->willReturn()
而不是 shouldBeCalled()
.