44

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?

user7282
  • 5,106
  • 9
  • 41
  • 72
RodriKing
  • 822
  • 2
  • 10
  • 20
  • 1
    documentation [explains on mocks](https://phpunit.de/manual/5.7/en/test-doubles.html#test-doubles.mock-objects) same well – xmike Aug 30 '17 at 19:11

3 Answers3

97

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();
Jakub Zalas
  • 35,761
  • 9
  • 93
  • 125
  • You don't need to explicitly reveal prophecies in PhpUnit? – everzet Aug 31 '17 at 07:53
  • You need to indeed! Updated the answer. Thanks! :) – Jakub Zalas Aug 31 '17 at 07:55
  • and What is the difference between createMock() and getMockBuilder(), in the documentation to make Mocks uses getMockBuilder() method. Does not matter? Thanks!!! – RodriKing Aug 31 '17 at 12:09
  • That's a different question ;) It's in the docs: "The createMock($type) method immediately returns a test double object for the specified type (interface or class). The creation of this test double is performed using best practice defaults (the __construct() and __clone() methods of the original class are not executed) and the arguments passed to a method of the test double will not be cloned. If these defaults are not what you need then you can use the getMockBuilder($type) method to customize the test double generation using a fluent interface." – Jakub Zalas Aug 31 '17 at 13:04
  • You can definitely define spies with phpunit mock objects, too: `$spy = $this->createMock(Foo::class); $spy->expect($this->once('bar'))->with($this->identicalTo('baz'))->willReturn('qux');`. If `$spy` is used as a collaborator, and never called with the corresponding argument, the test will fail. – localheinz Aug 31 '17 at 13:44
  • @localheinz expectations for spies are suppose to be defined AFTER the SUT was called. I don't think your example will work this way. There's another workaround but it's a workaround nevertheless. Spies are not supported by phpunit by default (other than with prophecy). See https://github.com/sebastianbergmann/phpunit-mock-objects/issues/333 – Jakub Zalas Aug 31 '17 at 14:34
  • Great!!! Mocks verify behavior (verify that method call) and Stubs verify state (verify the return value). – RodriKing Aug 31 '17 at 18:23
  • @JakubZalas After reading up on http://xunitpatterns.com/Test%20Spy.html I have to admit that I agree with you. I think the way I have been *arranging* test doubles in that regard has been more like “fail this test if you aren't sent messages as expected”, rather than *assert* after *acting* like “tell me if this and that message has been sent to you“. – localheinz Aug 31 '17 at 19:18
  • @localheinz these are simply mocks you're talking about. Spies are like mocks, but they make tests more natural to read and write (as expectations go last). – Jakub Zalas Sep 01 '17 at 08:03
6

Dummies

First, look at dummies. A dummy object is both what I look like if you ask me to remember where I left the car keys... and also the object you get if you add an argument with a type-hint in phpspec to get a test double... then do absolutely nothing with it. So if we get a test double and add no behavior and make no assertions on its methods, it's called a "dummy object".

Oh, and inside of their documentation, you'll see things like $prophecy->reveal(). That's a detail that we don't need to worry about because phpspec takes care of that for us. Score!

Stubs

As soon as you start controlling even one return value of even one method... boom! This object is suddenly known as a stub. From the docs: "a stub is an object double" - all of these things are known as test doubles, or object doubles - that when put in a specific environment, behaves in a specific way. That's a fancy way of saying: as soon as we add one of these willReturn() things, it becomes a stub.

And actually, most of the documentation is spent talking about stubs and the different ways to control exactly how it behaves, including the Argument wildcarding that we saw earlier.

Mocks

If you keep reading down, the next thing you'll find are "mocks". An object becomes a mock when you call shouldBeCalled(). So, if you want to add an assertion that a method is called a certain number of times and you want to put that assertion before the actual code - using shouldBeCalledTimes() or shouldBeCalled() - congratulations! Your object is now known as a mock.

Spies

And finally, at the bottom, we have spies. A spy is the exact same thing as a mock, except it's when you add the expectation after the code - like with shouldHaveBeenCalledTimes().

https://symfonycasts.com/screencast/phpspec/doubles-dummies-mocks-spies

Mhmd
  • 406
  • 5
  • 12
0

in short, we can say:

You can use a mock object “as an observation point that is used to verify the indirect outputs of the SUT as it is exercised. Typically, the mock object also includes the functionality of a test stub in that it must return values to the SUT if it hasn’t already failed the tests but the emphasis is on the verification of the indirect outputs. Therefore, a mock object is a lot more than just a test stub plus assertions; it is used in a fundamentally different way.

Alireza Rahmani Khalili
  • 2,727
  • 2
  • 32
  • 33