Testing factory method in command handler with php

2019-06-01 18:59发布

问题:

How to test static methods which are infact factory methods:

public function hireEmployee(HireEmployeeCommand $command)
{
    $username = new EmployeeUsername($command->getUsername());
    $firstName = $command->getFirstName();
    $lastName = $command->getLastName();
    $phoneNumber = new PhoneNumber($command->getPhoneNumber());
    $email = new Email($command->getEmail());
    $role = new EmployeeRole($command->getRole());

    if ($role->isAdmin()) {
        $employee = Employee::hireAdmin($username, $firstName, $lastName, $phoneNumber, $email);
    } else {
        $employee = Employee::hirePollster($username, $firstName, $lastName, $phoneNumber, $email);
    }

    $this->employeeRepository->add($employee);
}

Here I can't mock the Employee object, but I can mock the EmployeeRepository::add() method for the expected employee but then I'm again checking the state of the Employee:

public function it_hires_an_admin()
{
    $this->employeeRepository
        ->add(Argument::that(/** callback for checking state of Employee object */))
        ->shouldBeCalled();

    $this->hireEmployee(
        new HireEmployeeCommand(self::USERNAME, 'John', 'Snow', '123456789', 'john@snow.com', EmployeeRole::ROLE_ADMIN)
    );
}

I'm aware that again I mocked the repository instead of stub it. But here I'm more interested in employee will be added to the repository instead of how it would be created. Because of that I should mock the repository but I shouldn't care about the state of the Employee (without Argument::that())? Looks reasonable, but then I can't be sure that the created Employee is correct.

回答1:

You don't really need to stub nor mock your entities or value objects, as there's no behaviour they exhibit in the spec:

public function it_hires_an_admin()
{
    $this->employeeRepository
        ->add(Argument::is(
            Employee::hireAdmin(self::USERNAME, 'John', 'Snow', '123456789', 'john@snow.com')
        ))
        ->shouldBeCalled();

    $this->hireEmployee(
        new HireEmployeeCommand(
            self::USERNAME, 'John', 'Snow', '123456789', 'john@snow.com', EmployeeRole::ROLE_ADMIN
        )
    );
}