使用phpspec测试命令处理程序

时间:2015-06-27 07:36:35

标签: php testing command phpspec

最近我尝试了phpspec。它运行良好,但我遇到了测试命令处理程序的问题。例如,在PHPUnit中,我就这样测试:

/**
 * @test
 */
public function it_should_change_an_email()
{
    $this->repository->add($this->employee);

    $this->handler->changeEmail(
        new ChangeEmailCommand(
            $this->employee->username()->username(),
            'new@email.com'
        )
    );

    Asserts::assertEquals(new Email('new@email.com'), $this->employee->email());
}

并设置:

protected function setUp()
{
    $this->repository = new InMemoryEmployeeRepository();
    $this->createEmployee();

    $this->handler = new EmployeeCommandHandler($this->repository);
}

重点是此测试在Employee对象上进行断言以检查CommandHandler是否正常工作。但是在phpspec中,我不能在指定的对象上进行断言,在这种情况下,我只能在我的CommandHandler上进行断言。那么如何在phpspec中测试命令处理程序呢?

修改

也许间谍是要走的路:

class EmployeeCommandHandlerSpec extends ObjectBehavior
{
    const USERNAME = 'johnny';

    /** @var EmployeeRepository */
    private $employeeRepository;

    public function let(EmployeeRepository $employeeRepository)
    {
        $this->employeeRepository = $employeeRepository;
        $this->beConstructedWith($employeeRepository);
    }

    public function it_changes_the_employee_email(Employee $employee)
    {
        $this->givenEmployeeExists($employee);

        $this->changeEmail(
            new ChangeEmailCommand(self::USERNAME, 'new@email.com')
        );

        $employee->changeEmail(new Email('new@email.com'))->shouldHaveBeenCalled();
    }

    private function givenEmployeeExists(Employee $employee)
    {
        $this->employeeRepository->employeeWithUsername(new EmployeeUsername(self::USERNAME))
             ->shouldBeCalled()
             ->willReturn($employee);
    }
}    

员工班级I已经入职。所以,也许,在命令处理程序中,只需检查是否已调用Employee的方法即可。你怎么看待这件事?我是朝着好的方向前进的吗?

1 个答案:

答案 0 :(得分:3)

<强>信息

实际上,您不应该验证状态,而是期望对象之间的某些交互。这就是OOP关于事后的消息。

您在PHPUnit中完成它的方式是状态验证。它会强制你暴露状态,因为你需要提供一个&#34; getter&#34;,这并不总是需要的。您感兴趣的是员工的电子邮件已更新:

$employee->updateEmail(new Email('new@email.com'))->shouldBeCalled();

如果您愿意,可以通过间谍实现同样的目标:

$employee->updateEmail(new Email('new@email.com'))->shouldHaveBeenCalled();

命令/查询分离

我们通常只需要对具有副作用的方法(命令/查询分离的命令方法)说明我们的期望。我们嘲笑他们。

查询方法不需要模拟,而是存根。你真的不希望调用EmployeeRepository::employeeWithUsername()。这样做我们就实施做出了假设,这反过来会使重构变得更难。所有你需要的是存根,所以如果一个方法被调用它会返回一个结果:

$employeeRepository->employeeWithUsername(new EmployeeUsername(self::USERNAME))
    ->willReturn($employee);

完整示例

class EmployeeCommandHandlerSpec extends ObjectBehavior
{
    const USERNAME = 'johnny';

    public function let(EmployeeRepository $employeeRepository)
    {
        $this->beConstructedWith($employeeRepository);
    }

    public function it_changes_the_employee_email(
        EmployeeRepository $employees, Employee $employee
    ) {
        $this->givenEmployeeExists($employees, $employee);

        $this->changeEmail(
            new ChangeEmailCommand(self::USERNAME, 'new@email.com')
        );

        $employee->changeEmail(new Email('new@email.com'))->shouldHaveBeenCalled();
    }

    private function givenEmployeeExists(
        EmployeeRepository $employees, Employee $employee
    ) {
        $employees->employeeWithUsername(new EmployeeUsername(self::USERNAME))
             ->willReturn($employee);
    }
}