Search code examples
unit-testingtestingmockingphpunitstub

Mocks vs Stubs in PHPUnit


I know stubs verify state and the mocks verify behavior.

How can I make a mock in PHPUnit to verify the behavior of the methods? PHPUnit does not have verification methods (verify()), And I do not know how to make a mock in PHPUnit.

In the documentation, to create a stub is well explained:

// Create a stub for the SomeClass class.
$stub = $this->createMock(SomeClass::class);

// Configure the stub.
$stub
    ->method('doSomething')
    ->willReturn('foo');

// Calling $stub->doSomething() will now return 'foo'.
$this->assertEquals('foo', $stub->doSomething());

But in this case, I am verifying status, saying that return an answer.

How would be the example to create a mock and verify behavior?


Solution

  • PHPUnit used to support two ways of creating test doubles out of the box. Next to the legacy PHPUnit mocking framework we could choose prophecy as well.

    Prophecy support was removed in PHPUnit 9, but it can be added back by installing phpspec/prophecy-phpunit.

    PHPUnit Mocking Framework

    The createMock method is used to create three mostly known test doubles. It's how you configure the object makes it a dummy, a stub, or a mock.

    You can also create test stubs with the mock builder (getMockBuilder returns the mock builder). It's just another way of doing the same thing that lets you to tweak some additional mock options with a fluent interface (see the documentation for more).

    Dummy

    Dummy is passed around, but never actually called, or if it's called it responds with a default answer (mostly null). It mainly exists to satisfy a list of arguments.

    $dummy = $this->createMock(SomeClass::class);
    
    // SUT - System Under Test
    $sut->action($dummy);
    

    Stub

    Stubs are used with query like methods - methods that return things, but it's not important if they're actually called.

    $stub = $this->createMock(SomeClass::class);
    $stub->method('getSomething')
        ->willReturn('foo');
    
    $sut->action($stub);
    

    Mock

    Mocks are used with command like methods - it's important that they're called, and we don't care much about their return value (command methods don't usually return any value).

    $mock = $this->createMock(SomeClass::class);
    $mock->expects($this->once())
        ->method('doSomething')
        ->with('bar');
    
    $sut->action($mock);
    

    Expectations will be verified automatically after your test method finished executing. In the example above, the test will fail if the method doSomething wasn't called on SomeClass, or it was called with arguments different to the ones you configured.

    Spy

    Not supported.

    Prophecy

    Prophecy is now supported by PHPUnit out of the box, so you can use it as an alternative to the legacy mocking framework. Again, it's the way you configure the object makes it becomes a specific type of a test double.

    Dummy

    $dummy = $this->prophesize(SomeClass::class);
    
    $sut->action($dummy->reveal());
    

    Stub

    $stub = $this->prophesize(SomeClass::class);
    $stub->getSomething()->willReturn('foo');
    
    $sut->action($stub->reveal());
    

    Mock

    $mock = $this->prophesize(SomeClass::class);
    $mock->doSomething('bar')->shouldBeCalled();
    
    $sut->action($mock->reveal());
    

    Spy

    $spy = $this->prophesize(SomeClass::class);
    
    // execute the action on system under test
    $sut->action($spy->reveal());
    
    // verify expectations after 
    $spy->doSomething('bar')->shouldHaveBeenCalled();