2
votes

Creating mock classes usually involves configuring method call expectations on the mocks/test doubles.

E.g. in 'vanilla' PHPUnit we can stub a method call and set an expectations like so:

$stub->expects($this->any())->method('doSomething')->willReturn('foo');

In the Mockery mock object framework, we get the API like this:

$mock->shouldReceive('doIt')->with(m::anyOf('this','that'))->andReturn($this->getSomething());

Expectations like these, are often wired in the set-up phase of a test-suite, e.g. the setUp() method of \PHPUnit_Framework_TestCase.

Expectations like the ones presented above would break the test if they are not fulfilled. Consequently, making the expectations to be actual assertions.

This leads to the situation where we have assertions (assertions + expectations) scattered around the test case class since we end up having actual assertions in the set-up phase of a test case as well as in individual tests.

Would it be good practice to test method call expectations in 'regular' assert.. method. This might look like that (Mockery):

public function setUp()
{
    $mock = m::mock(SomeClass::class);
    $mock->shouldReceive('setSomeValue');
    $this->mock = $mock;
}

and later at the end of one of the test methods:

public function testSoemthing()
{ 
    ...
    $this->assertMethodCalled($this->mock, 'setSomeValue');
}

assertMethodCalled in not a method exposed by PHPUnit. It'd have to be implemented.

In short, should we treat expectation declarations as actual assertions and consequently test against them in our test methods?

1

1 Answers

2
votes

You don't need to configure your test doubles (stubs/mocks) in the setUp() method.

Actually, I would never configure a mock in test setup code and I would only put very common stubs in there. Mock expectations are very often different for each test case. I'd rather instantiate my test doubles in the setup code, assign them to a private properties and configure them in each test case.

private $registration;
private $repository;

protected function setUp()
{
    $this->repository = $this->getMock(UserRepository::class);
    $this->registration = new Registration($repository);
}

public function testUserIsAddedToTheRepositoryDuringRegistration()
{
    $user = $this->createUser();

    $this->repository
         ->expects($this->once())
         ->method('add')
         ->with($user);

    $this->registration->register($user);
}

So it's fine to put your test double configurations in the test case. It's actually better as your test case has all the context and therefore is more readable. If you find yourself configuring lots of test doubles or writing long test cases, this might mean you have too many collaborators and you should think how to improve your design. You can also use helper methods with meaningful names to make your test cases more readable - i.e. $this->givenExistingUser() or $this->givenRepositoryWithNoUsers().

As you noticed, mock expectations (don't confuse them with stubs which are not expectations) are very close to assertions. There's actually a way to accomplish what you're looking for with another type of test doubles - spies.

Spies are not supported by the classic phpunit mocking framework. However, PHPUnit has a built in support for prophecy now. Luckily, prophecy supports spies. Here's an example:

private $registration;
private $repository;

protected function setUp()
{
    $this->repository = $this->prophesize(UserRepository::class);
    $this->registration = new Registration($repository->reveal());
}

public function testUserIsAddedToTheRepositoryDuringRegistration()
{
    $user = $this->createUser();

    $this->registration->register($user);

    $this->repository->add($user)->shouldHaveBeenCalled();
}

On a final note, I avoid assertions and mock expectations in the same test case. Most of the time these are separate behaviours that should be covered with distinct test cases.

To learn more about test doubles read the excellent PHP Test Doubles Patterns.