One of the code snippets you will see in the article

Clean mocking in your PHPUnit tests using Prophecy and Given-When-Then approach

Piotr Macha
Owl IT Development
Published in
4 min readMay 24, 2018

--

Your tests are your project’s specification. They show you the rules of how your code should work and to do so they need to be clean and written in a such way you can read them as a book. But often they aren’t and instead of good description of the rules, we see ugly code consisting of long and weird mock definitions that will break if you do a method name refactor or if you make a typo, because they keep the names as raw string. In this short article, I will show you Prophecy — great object mocking framework that will make your tests beautiful.

Installation

Prophecy is available through composer so if your project use it just type the following command:

If you are not using composer, consider doing so because it’s an industry standard.

Our scenario

We are doing an advanced AI that verifies attenders at a club party and decides who can get in and if they are eligible for a discount. It must follow the rules:

  • Gatekeeper verifies one person at a time by taking it from queue
  • If attender is under 18, they can not get in
  • If attender is at least 18, they enter
  • If attender is a student under 26, they get 20% discount
  • If there are no more people in queue, gatekeeper should stop

Look at the following code, our target is to write a tests for the ClubGatekeeper::verifyAttenders() method. We will tests if the rules are met using Prophecy mocks.

Writing a Test

First of all, we need our test case template in which we create an instance of Prophet we will use to create mocks. You can do it in setUp() method and remember to call Prophet’s checkPreconditons() in tearDown() if you want to have assertions in your mocks.

Mocking — return values

First we create a mock using Prophet and then we can define behavior on our mock objects using really nice interface. Basically it looks like you call a method on your interface and then call returnValue() to set a static list of function results. You can put as many as you like. Next argument will be returned for next call.

Mocking — calls amount

You can also set more conditions like the amount of calls. Let’s say we want to check if our gatekeeper checks queue only twice. We can accomplish it using shouldBeCalledTimes() method.

Mocking — call arguments

Another important feature of mocks is the ability to put conditions on what they should get as arguments. We can do it by putting static values in the mock method call or by using Argument class providing more flexibility. Look at the $party mock.

Passing the mock to tested class

Our mock in definition process in not yet an object implementing our interface, but an instance of ObjectProphecy. To pass the mock to tested code, we need to call reveal() on it.

This code is basically just a working test. Gatekeeper will operate on the mocks and after its execution the mock conditions are checked in PHPUnit’s tearDown() as we make it so few snippets before.

Given-When-Then

Given-When-Then is a popular test writing style allowing you to have the test cases very organised. Basically you split the test into 3 steps.

  • Given: you define the input data for given case
  • When: you execute the code that should do a business operation
  • Then: you validate if everything went as it should have

This style is trivial to implement if we don’t mock but it can be hard if we do. That’s because usually mocking frameworks require to set up the mock conditions before we call the When part while validation should happen after it. Prophecy is different — it analyses what is happening to the mock all the time and provides methods with “have” (in past tense meaning) that allow you to verify conditions after they have happened.

Look how we can implement Given-When-Then to our test:

And now try to read it using natural language.

Given an attender of age 21 who is a student and a queue of this attender only, when gatekeeper verifies the queue for a party, then queue should have been checked for an attender 2 times and the party should have been added an attender with 20% discount once.

It sounds like a specification. And that is what our tests should be — a clean specifications.

And it’s needed to say that the shown code is not the most clean solution for a given problem. We might separate the queue iteration from the single attender processing and then test the Gatekeeper class using only one mock, but main purpose of this article was to show Prophecy.

Worth checking

--

--