Mockery: A Better Way

Mockery: A Better Way

Tutorial Details
    • Programming Language: PHP
    • Difficulty: Intermediate
    • Completion Time: 2 Hour
This entry is part 12 of 12 in the Test-Driven PHP Session
« Previous

Mockery is a PHP extension that offers a superior mocking experience, particularly when compared to PHPUnit. While PHPUnit’s mocking framework is powerful, Mockery offers a more natural language with a Hamcrest-like set of matchers. In this article, I’ll compare the two mocking frameworks and highlight the best features of Mockery.

Mockery offers a set of mocking-related matchers that are very similar to a Hamcrest dictionary, offering a very natural way to express mocked expectations. Mockery does not override or conflict with PHPUnit’s built-in mocking functions; in fact, you can use them both at the same time (and even in the same test method).


Installing Mockery

There are multiple ways to install Mockery; here are the most common methods.

Use Composer

Create a file called composer.json in you project’s root folder, and add the following code to that file:

{
    "require": {
        "Mockery/Mockery": ">=0.7.2"
    }
}

Next, simply install Composer in your project’s root folder using the following command:

curl -s http://getcomposer.org/installer | php

Finally, install any required dependencies (including Mockery) with this command:

php composer.phar install

With everything installed, let’s ensure that our Mockery installation works. For the sake of simplicity, I’ll assume that you have a folder, called Test in your project’s root directory. All of the examples in this tutorial will reside in this folder. Here’s the code I’ve used to ensure that Mockery works with my project:

//Filename: JustToCheckMockeryTest.php

require_once '../vendor/autoload.php';

class JustToCheckMockeryTest extends PHPUnit_Framework_TestCase {

	protected function tearDown() {
		\Mockery::close();
	}
	function testMockeryWorks() {
		$mock = \Mockery::mock('AClassToBeMocked');
		$mock->shouldReceive('someMethod')->once();

		$workerObject = new AClassToWorkWith;
		$workerObject->doSomethingWit($mock);
	}
}

class AClassToBeMocked {}

class AClassToWorkWith {

	function doSomethingWit($anotherClass) {
		return $anotherClass->someMethod();
	}

}

Linux Users: Use Your Distro’s Packages

Some Linux distributions make it easy to install Mockery, but only a handful provide a Mockery package for their system. The following list are the only distros I’m aware of:

  • Sabayon: equo install Mockery
  • Fedora / RHE: yum install Mockery

Use PEAR

PEAR fans can install Mockery by issuing the following commands:

sudo pear channel-discover pear.survivethedeepend.com
sudo pear channel-discover hamcrest.googlecode.com/svn/pear
sudo pear install --alldeps deepend/Mockery

Installing from Source

Installing from GitHub is for the real geeks! You can always grab the latest version of Mockery through its GitHub repository.

git clone git://github.com/padraic/Mockery.git
cd Mockery
sudo pear channel-discover hamcrest.googlecode.com/svn/pear
sudo pear install --alldeps package.xml

Creating our First Mocked Object

Let’s mock some objects before we define any expectations. The following code will modify the previous example to include both PHPUnit and Mockery examples:

//Filename: MockeryABetterWayOfMockingTest.php
require_once '../vendor/autoload.php';

class MockeryVersusPHPUnitGetMockTest extends PHPUnit_Framework_TestCase {

	function testCreateAMockedObject() {
		// With PHPUnit
		$phpunitMock = $this->getMock('AClassToBeMocked');

		// With Mockery
		$mockeryMock = \Mockery::mock('AClassToBeMocked');
	}

}

class AClassToBeMocked {

}

Mockery allows you to define mocks for classes that do not exist.

The first line ensures that we have access to Mockery. Next, we create a test class, called MockeryVersusPHPUnitGetMockTest, which has a method, testCreateAMockedObject(). The mocked class, AClassToBeMocked, is completely empty at this time; in fact, you could completely remove the class without causing the test to fail.

The testCreateAMockedObject() test method defines two objects. The first is a PHPUnit mock, and the second is created with Mockery. Mockery’s syntax is:

$mockedObject = \Mockery::mock('SomeClassToBeMocked');

Assign Simple Expectations

Mocks are commonly used to verify an object’s behavior (primarily its methods) by specifying what are called expectations. Let’s set up a few simple expectations.

Expect a Method to be Called

Probably the most common expectation is one that expects a specific method call. Most mocking frameworks allow you to specify the amount of calls you expect a method to receive. We’ll begin with a simple single call expectation:

//Filename: MockeryABetterWayOfMockingTest.php
require_once '../vendor/autoload.php';

class MockeryVersusPHPUnitGetMockTest extends PHPUnit_Framework_TestCase {

	protected function tearDown() {
		\Mockery::close();
	}

	function testExpectOnce() {
		$someObject = new SomeClass();

		// With PHPUnit
		$phpunitMock = $this->getMock('AClassToBeMocked');
		$phpunitMock->expects($this->once())->method('someMethod');
		// Exercise for PHPUnit
		$someObject->doSomething($phpunitMock);

		// With Mockery
		$mockeryMock = \Mockery::mock('AnInexistentClass');
		$mockeryMock->shouldReceive('someMethod')->once();
		// Exercise for Mockery
		$someObject->doSomething($mockeryMock);
	}

}

class AClassToBeMocked {
	function someMethod() {}
}

class SomeClass {
	function doSomething($anotherObject) {
		$anotherObject->someMethod();
	}
}

This code configures an expectation for both PHPUnit and Mockery. Let’s start with the former.

Some Linux distributions make it easy to install Mockery.

We use the expects() method to define an expectation to call someMethod() once. But in order for PHPUnit to work correctly, we must define a class called AClassToBeMocked, and it must have a someMethod() method.

This is a problem. If you are mocking a lot of objects and developing using TDD principles for a top-down design, you would not want to create all the classes and methods before your test. Your test should fail for the right reason, that the expected method was not called, instead of a critical PHP error with no relation to the real implementation. Go ahead and try to remove the someMethod() definition from AClassToBeMocked, and see what happens.

Mockery, on the other hand, allows you to define mocks for classes that do not exist.

Notice that the above example creates a mock for AnInexistentClass, which as its name implies, does not exist (nor does its someMethod() method).

At the end of the above example, we define the SomeClass class to exercise our code. We initialize an object, called $someObject in the first line of the test method, and we effectively exercise the code after defining our expectations.

Please Note: Mockery evaluates expectations on its close() method. For this reason, you should always have a tearDown() method on your test that calls \Mockery::close(). Otherwise, Mockery gives false positives.

Expect More Than One Call

As I noted previously, most mocking frameworks have the ability to specify expectations for multiple method calls. PHPUnit uses the $this->exactly() construct for this purpose. The following code defines the expectations for calling a method multiple times:

function testExpectMultiple() {
	$someObject = new SomeClass();

	// With PHPUnit 2 times
	$phpunitMock = $this->getMock('AClassToBeMocked');
	$phpunitMock->expects($this->exactly(2))->method('someMethod');
	// Exercise for PHPUnit
	$someObject->doSomething($phpunitMock);
	$someObject->doSomething($phpunitMock);

	// With Mockery 2 times
	$mockeryMock = \Mockery::mock('AnInexistentClass');
	$mockeryMock->shouldReceive('someMethod')->twice();
	// Exercise for Mockery
	$someObject->doSomething($mockeryMock);
	$someObject->doSomething($mockeryMock);

	// With Mockery 3 times
	$mockeryMock = \Mockery::mock('AnInexistentClass');
	$mockeryMock->shouldReceive('someMethod')->times(3);
	// Exercise for Mockery
	$someObject->doSomething($mockeryMock);
	$someObject->doSomething($mockeryMock);
	$someObject->doSomething($mockeryMock);
}

Mockery provides two different methods to better suit your needs. The first method, twice(), expects two method calls. The other method is times(), which lets you specify an amount. Mockery’s approach is much cleaner and easier to read.


Returning Values

Another common use for mocks is to test a method’s return value. Naturally, both PHPUnit and Mockery have the means to verify return values. Once again, let’s start with something simple.

Simple Return Values

The following code contains both PHPUnit and Mockery code. I also updated SomeClass to provide a testable return value.

class MockeryVersusPHPUnitGetMockTest extends PHPUnit_Framework_TestCase {

	protected function tearDown() {
		\Mockery::close();
	}

	// [...] //

	function testSimpleReturnValue() {
		$someObject = new SomeClass();
		$someValue = 'some value';

		// With PHPUnit
		$phpunitMock = $this->getMock('AClassToBeMocked');
		$phpunitMock->expects($this->once())->method('someMethod')->will($this->returnValue($someValue));
		// Expect the returned value
		$this->assertEquals($someValue, $someObject->doSomething($phpunitMock));
		// With Mockery
		$mockeryMock = \Mockery::mock('AnInexistentClass');
		$mockeryMock->shouldReceive('someMethod')->once()->andReturn($someValue);
		// Expect the returned value
		$this->assertEquals($someValue, $someObject->doSomething($mockeryMock));
	}

}

class AClassToBeMocked {

	function someMethod() {

	}

}

class SomeClass {

	function doSomething($anotherObject) {
		return $anotherObject->someMethod();
	}

}

Both PHPUnit’s and Mockery’s API is straight-forward and easy to use, but I still find Mockery to be cleaner and more readable.

Returning Different Values

Frequent unit testers can testify to complications with methods that return different values. Unfortunately, PHPUnit’s limited $this->at($index) method is the only way to return different values from the same method. The following code demonstrates the at() method:

function testDemonstratePHPUnitCallIndexing() {
	$someObject = new SomeClass();
	$firstValue = 'first value';
	$secondValue = 'second value';

	// With PHPUnit
	$phpunitMock = $this->getMock('AClassToBeMocked');
	$phpunitMock->expects($this->at(0))->method('someMethod')->will($this->returnValue($firstValue));
	$phpunitMock->expects($this->at(1))->method('someMethod')->will($this->returnValue($secondValue));
	// Expect the returned value
	$this->assertEquals($firstValue, $someObject->doSomething($phpunitMock));
	$this->assertEquals($secondValue, $someObject->doSomething($phpunitMock));

}

This code defines two separate expectations and makes two different calls to someMethod(); so, this test passes. But let’s introduce a twist and add a double call in the tested class:

// [...] //
function testDemonstratePHPUnitCallIndexingOnTheSameClass() {
	$someObject = new SomeClass();
	$firstValue = 'first value';
	$secondValue = 'second value';

	// With PHPUnit
	$phpunitMock = $this->getMock('AClassToBeMocked');
	$phpunitMock->expects($this->at(0))->method('someMethod')->will($this->returnValue($firstValue));
	$phpunitMock->expects($this->at(1))->method('someMethod')->will($this->returnValue($secondValue));
	// Expect the returned value
	$this->assertEquals('first value second value', $someObject->concatenate($phpunitMock));

}

class SomeClass {

	function doSomething($anotherObject) {
		return $anotherObject->someMethod();
	}

	function concatenate($anotherObject) {
		return $anotherObject->someMethod() . ' ' . $anotherObject->someMethod();
	}

}

The test still passes. PHPUnit expects two calls to someMethod() that happen inside the tested class when performing the concatenation via the concatenate() method. The first call returns the first value, and the second call returns the second value. But, here’s the catch: what would happen if you double the assertion? Here’s the code:

function testDemonstratePHPUnitCallIndexingOnTheSameClass() {
	$someObject = new SomeClass();
	$firstValue = 'first value';
	$secondValue = 'second value';

	// With PHPUnit
	$phpunitMock = $this->getMock('AClassToBeMocked');
	$phpunitMock->expects($this->at(0))->method('someMethod')->will($this->returnValue($firstValue));
	$phpunitMock->expects($this->at(1))->method('someMethod')->will($this->returnValue($secondValue));
	// Expect the returned value
	$this->assertEquals('first value second value', $someObject->concatenate($phpunitMock));
	$this->assertEquals('first value second value', $someObject->concatenate($phpunitMock));

}

It returns the following error:

Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'first value second value'
+' '

PHPUnit continues counting between distinct calls to concatenate(). By the time the second call in the last assertion occurs, $index is at the values 2 and 3. You can make the test pass by modifying your expectations to consider the two new steps, like this:

function testDemonstratePHPUnitCallIndexingOnTheSameClass() {
	$someObject = new SomeClass();
	$firstValue = 'first value';
	$secondValue = 'second value';

	// With PHPUnit
	$phpunitMock = $this->getMock('AClassToBeMocked');
	$phpunitMock->expects($this->at(0))->method('someMethod')->will($this->returnValue($firstValue));
	$phpunitMock->expects($this->at(1))->method('someMethod')->will($this->returnValue($secondValue));
	$phpunitMock->expects($this->at(2))->method('someMethod')->will($this->returnValue($firstValue));
	$phpunitMock->expects($this->at(3))->method('someMethod')->will($this->returnValue($secondValue));
	// Expect the returned value
	$this->assertEquals('first value second value', $someObject->concatenate($phpunitMock));
	$this->assertEquals('first value second value', $someObject->concatenate($phpunitMock));

}

You can probably live with this code, but Mockery makes this scenario trivial. Don’t believe me? Take a look:

function testMultipleReturnValuesWithMockery() {
	$someObject = new SomeClass();
	$firstValue = 'first value';
	$secondValue = 'second value';

	// With Mockery
	$mockeryMock = \Mockery::mock('AnInexistentClass');
	$mockeryMock->shouldReceive('someMethod')->andReturn($firstValue, $secondValue, $firstValue, $secondValue);

	// Expect the returned value
	$this->assertEquals('first value second value', $someObject->concatenate($mockeryMock));
	$this->assertEquals('first value second value', $someObject->concatenate($mockeryMock));
}

Like PHPUnit, Mockery uses index counting, but we don’t have to worry about indices. Instead, we simply list all the expected values, and Mockery returns them in order.

Additionally, PHPUnit returns NULL for unspecified indexes, but Mockery always returns the last specified value. That’s a nice touch.

Try Multiple Methods with Indexing

Let’s introduce a second method into our code, the concatWithMinus() method:

class SomeClass {

	function doSomething($anotherObject) {
		return $anotherObject->someMethod();
	}

	function concatenate($anotherObject) {
		return $anotherObject->someMethod() . ' ' . $anotherObject->someMethod();
	}

	function concatWithMinus($anotherObject) {
		return $anotherObject->anotherMethod() . ' - ' . $anotherObject->anotherMethod();
	}

}

This method behaves similarly to concatenate(), but it concatenates the string values with “ - ” as opposed to a single space. Because these two methods perform similar tasks, it makes sense to to test them inside the same test method to avoid duplicate testing.

As demonstrated in the above code, the second function uses a different mocked method called anotherMethod(). I made this change to force us to mock both methods in our tests. Our mockable class now looks like this:

class AClassToBeMocked {

	function someMethod() {

	}

	function anotherMethod() {

	}

}

Testing this with PHPUnit might look like the following:

function testPHPUnitIndexingOnMultipleMethods() {
	$someObject = new SomeClass();
	$firstValue = 'first value';
	$secondValue = 'second value';

	// With PHPUnit
	$phpunitMock = $this->getMock('AClassToBeMocked');

	// First and second call on the semeMethod:
	$phpunitMock->expects($this->at(0))->method('someMethod')->will($this->returnValue($firstValue));
	$phpunitMock->expects($this->at(1))->method('someMethod')->will($this->returnValue($secondValue));
	// Expect the returned value
	$this->assertEquals('first value second value', $someObject->concatenate($phpunitMock));

	// First and second call on the anotherMethod:
	$phpunitMock->expects($this->at(0))->method('anotherMethod')->will($this->returnValue($firstValue));
	$phpunitMock->expects($this->at(1))->method('anotherMethod')->will($this->returnValue($secondValue));
	// Expect the returned value
	$this->assertEquals('first value - second value', $someObject->concatWithMinus($phpunitMock));
}

The logic is sound. Define two different expectations for each method and specify the return value. This works only with PHPUnit 3.6 or newer.

Please Note: PHPunit 3.5 and older had a bug which did not reset the index for each method, resulting in unexpected return values for mocked methods.

Let’s look at the same scenario with Mockery. Once again, we get much cleaner code. See for yourself:

function testMultipleReturnValuesForDifferentFunctionsWithMockery() {
	$someObject = new SomeClass();
	$firstValue = 'first value';
	$secondValue = 'second value';

	// With Mockery
	$mockeryMock = \Mockery::mock('AnInexistentClass');
	$mockeryMock->shouldReceive('someMethod')->andReturn($firstValue, $secondValue);
	$mockeryMock->shouldReceive('anotherMethod')->andReturn($firstValue, $secondValue);

	// Expect the returned value
	$this->assertEquals('first value second value', $someObject->concatenate($mockeryMock));
	$this->assertEquals('first value - second value', $someObject->concatWithMinus($mockeryMock));
}

Return Values Based on Given Parameter

Honestly, this is something PHPUnit simply cannot do. At the time of this writing, PHPUnit does not permit you to return different values from the same function based on the function’s parameter. Therefore, the following test fails:

// [...] //
function testPHUnitCandDecideByParameter() {
	$someObject = new SomeClass();

	// With PHPUnit
	$phpunitMock = $this->getMock('AClassToBeMocked');
	$phpunitMock->expects($this->any())->method('getNumber')->with(2)->will($this->returnValue(2));
	$phpunitMock->expects($this->any())->method('getNumber')->with(3)->will($this->returnValue(3));

	$this->assertEquals(4, $someObject->doubleNumber($phpunitMock, 2));
	$this->assertEquals(6, $someObject->doubleNumber($phpunitMock, 3));

}

class AClassToBeMocked {

// [...] //
	function getNumber($number) {
		return $number;
	}
}

class SomeClass {

	// [...] //

	function doubleNumber($anotherObject, $number) {
		return $anotherObject->getNumber($number) * 2;
	}
}

Please ignore the fact that there is no logic in this example; it would fail even if it was present. This code, however, does help illustrate the idea.

This test fails because PHPUnit cannot differentiate between the two expectations in the test. The second expectation, expecting parameter 3, simply overrides the first expecting parameter 2. If you attempt to run this test, you get the following error:

Expectation failed for method name is equal to <string:getNumber> when invoked zero or more times
Parameter 0 for invocation AClassToBeMocked::getNumber(2) does not match expected value.
Failed asserting that 2 matches expected 3.

Mockery can do this, and the code below works exactly as you would expect it to work. The method returns different values based on its provided parameters:

function testMockeryReturningDifferentValuesBasedOnParameter() {
	$someObject = new SomeClass();

	// Mockery
	$mockeryMock = \Mockery::mock('AnInexistentClass');
	$mockeryMock->shouldReceive('getNumber')->with(2)->andReturn(2);
	$mockeryMock->shouldReceive('getNumber')->with(3)->andReturn(3);

	$this->assertEquals(4, $someObject->doubleNumber($mockeryMock, 2));
	$this->assertEquals(6, $someObject->doubleNumber($mockeryMock, 3));
}

Partial Mocks

Sometimes, you want to mock only specific methods on your object (as opposed to mocking an entire object). The following Calculator class already exists; we want to only mock certain methods:

class Calculator {
	function add($firstNo, $secondNo) {
		return $firstNo + $secondNo;
	}

	function subtract($firstNo, $secondNo) {
		return $firstNo - $secondNo;
	}

	function multiply($value, $multiplier) {
		$newValue = 0;
		for($i=0;$i<$multiplier;$i++)
			$newValue = $this->add($newValue, $value);
		return $newValue;
	}
}

This Calculator class has three methods: add(), subtract(), and multiply(). Multiply uses a loop to perform the multiplication by calling the add() for a specified amount of times (e.g. 2 x 3 is really 2 + 2 + 2).

Let’s assume that we want to test multiply() in total isolation; so, we’ll mock add() and check for specific behavior on multiply(). Here are some possible tests:

function testPartialMocking() {
	$value = 3;
	$multiplier = 2;
	$result = 6;

	// PHPUnit
	$phpMock = $this->getMock('Calculator', array('add'));
	$phpMock->expects($this->exactly(2))->method('add')->will($this->returnValue($result));

	$this->assertEquals($result, $phpMock->multiply($value,$multiplier));

	// Mockery
	$mockeryMock = \Mockery::mock(new Calculator);
	$mockeryMock->shouldReceive('add')->andReturn($result);

	$this->assertEquals($result, $mockeryMock->multiply($value,$multiplier));

	// Mockery extended test checking parameters
	$mockeryMock2 = \Mockery::mock(new Calculator);
	$mockeryMock2->shouldReceive('add')->with(0,3)->andReturn(3);
	$mockeryMock2->shouldReceive('add')->with(3,3)->andReturn(6);

	$this->assertEquals($result, $mockeryMock2->multiply($value,$multiplier));
}

Mockery offers…a very natural way to express mocked expectations.

The first PHPUnit test is anemic; it simply tests that the method add() is called twice and it returns the final value on each call. It gets the job done, but it’s also a little complicated. PHPUnit forces you to pass the list of methods that you want to mock as second parameter to $this->getMock(). Otherwise, PHPUnit would mock all methods, each returning NULL by default. This list must be kept in concordance with the expectations you define on your mocked object.

For example, if I add a second expectation to $phpMock‘s substract() method, PHPUnit would ignore it and call the original substract() method. That is, unless I explicitly specify the name of the method (substract) in the $this->getmock() statement.

Of course, Mockery is different by allowing you to provide a real object to \Mockery::mock(), and it automatically creates a partial mock. It achieves this by implementing a proxy-like solution for mocking. All the expectations you define are used, but Mockery falls back to the original method if you do not specify an expectation for that method.

Please Note: Mockery’s approach is very simple, but internal method calls do not pass through the mocked object.

This example is misleading, but it illustrates how not to use Mockery’s partial mocks. Yes, Mockery creates a partial mock if you pass a real object, but it only mocks only external calls. For example, based on the previous code, the multiply() method calls the real add() method. Go ahead and try to change the last expectation from ...->andReturn(6) to ...->andReturn(7). The test should obviously fail, but it doesn’t because the real add() executes instead of the mocked add() method.

But we can circumvent this issue by creating mocks like this:

//Instead of
$mockeryMock = \Mockery::mock(new Calculator);
// Create the mock like this
$mockeryMock = \Mockery::mock('Calculator[add]');

While syntactically different, the concept is similar to PHPUnit’s approach: you have to list the mocked methods in two places. But for any other test, you can just simply pass the real object, which is much easier–especially when dealing with constructor parameters.


Dealing with Constructor Parameters

Let’s add a constructor with two parameters to the Calculator class. The revised code:

class Calculator {
	public $myNumbers = array();

	function __construct($firstNo, $secondNo) {
		$this->myNumbers[]=$firstNo;
		$this->myNumbers[]=$secondNo;
	}
	// [...] //
}

Every test in this article will fail after adding this constructor. More precisely, the testPartialMock() test results in the following error:

Missing argument 1 for Calculator::__construct(),
called in /usr/share/php/PHPUnit/Framework/MockObject/Generator.php
on line 224 and defined

PHPUnit tries to mock the real object by automatically calling the constructor, expecting to have the parameters correctly set. There are two ways around this problem: either set the parameters, or don’t call the constructor.

//Specify Constructor Parameters
$phpMock = $this->getMock('Calculator', array('add'), array(1,2));

//Do not call original constructor
$phpMock = $this->getMock('Calculator', array('add'), array(), '', false);

Mockery automagically works around this problem. It’s okay not to specify a constructor parameter; Mockery will simply not call the constructor. But you can specify a list of constructor parameters for Mockery to use. For example:

function testMockeryConstructorParameters() {
	$result = 6;
	// Mockery
	// Do not call constructor
	$noConstrucCall = \Mockery::mock('Calculator[add]');
	$noConstrucCall->shouldReceive('add')->andReturn($result);

	// Use constructor parameters
	$withConstructParams = \Mockery::mock('Calculator[add]', array(1,2));
	$withConstructParams->shouldReceive('add')->andReturn($result);

	// User real object with real values and mock over it
	$realCalculator = new Calculator(1,2);
	$mockRealObj = \Mockery::mock($realCalculator);
	$mockRealObj->shouldReceive('add')->andReturn($result);
}

Technical Considerations

Mockery is another library that integrates your tests, and you may want to consider what technical implications this may have.

  • Mockery uses a lot of memory. You will have to increase the maximum memory to 512MB if you want to run many tests(say over 1000 tests with more than 3000 assertions). See php.ini documentation for further details.
  • You have to organize your tests to run in separate processes, when mocking static methods and static method calls.
  • You can auto-load Mockery into every test by using PHPUnit’s bootstrap functionality (helpful when you have many tests and you don’t want to repeat yourself).
  • You can automate the call to \Mockery::close() in each test’s tearDown() by editing phpunit.xml.

Final Conclusions

PHPUnit certainly has its issues, especially when it comes to functionality and expressiveness. Mockery can greatly improve your mocking experience by making your tests easy to write and understand – but it’s not perfect (there’s no such thing!).

This tutorial has highlighted many key aspects of Mockery, but, honestly, we’ve barely scratched the surface. Be sure to explore the project’s Github repository to learn more.

Thanks for reading!

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • j_blotus

    FYI PHPUnit mock objects can be created from classes that don’t exist as well

    • Patkos Csaba
      Author

      Would you be kind and provide an example? Maybe a link to some source code?

      • j_blotus

        https://github.com/sebastianbergmann/phpunit-mock-objects/blob/master/PHPUnit/Framework/MockObject/Generator.php

        look at the method generateMock();

        i have been using this functionality for years

      • Patkos Csaba
        Author

        Thanks, I wasn’t aware of that. I always just used $this->getMock(…) which indeed does not work with inexistent classes.

      • j_blotus

        I’m sorry man but your still wrong, getMock() calls this method internally. Php unit will create mocks from classes that don’t exist if the class name you pass in to getMock() is not defined, and the same thing with methods that don’t exist.

      • Patkos Csaba
        Author

        Still … I don’t know why but both at work and at home it does not work for me. It simply says an error that the class does not exist (can’t copy/paste it right now).

      • j_blotus

        public function testMock()
        {
        $sut = $this->getMock(‘IDontExist’);
        var_dump($sut);
        }
        //
        //object(Mock_IDontExist_e5b70242)#387 (1) {
        // ["invocationMocker":protected]=> NULL
        //}

      • Patkos Csaba
        Author

        Correct but it will crash in the second you put an expectation on it. At least this code is not working on my computer:
        $testMock = $this->getMock(‘InexistentClass’);
        $testMock->expect($this->once())->method(‘InexistentMethod’);

        PHP Fatal error: Call to undefined method Mock_InexistentClass_35117adc::expect() in…

      • j_blotus

        that is because you have to also create the methods (since it is a non-existent class and the mock has nothing to extend):

        $testMock = $this->getMock(‘InexistentClass’, array(‘InexistentMethod’));
        $testMock->expects($this->any())->method(‘InexistentMethod’);

        you can also use the mockbuilder interface for more clarity here:

        $testMock = $this->getMockBuilder(‘InexistentClass’)
        ->setMethods(array(‘InexistentMethod’))
        ->getMock();

      • Patkos Csaba
        Author

        Got your point. Thanks for the info.

    • Blacksonic

      Why is it even good to mock a non existing class?
      btw found eval in that clause when class doesnt exist…pure ugliness

      • Patkos Csaba
        Author

        When you are doing TDD in a top-down style development you start by creating a test for the classes you are about to implement. These classes do not yet exist and you use the test to actually define them and the collaborations between them.

      • Blacksonic

        one test for one class, when the test fails for not existing class u create an empty class for it or an interface, then the test can run with a red bar

      • Patkos Csaba
        Author

        One test for one class that is supposed to work with 5 other classes that do not exist yet. You can make your tested class and all it’s function without ever having the other 5 classes. When you test behavior (not state) this is a usual case.

        Another example is when you program something that uses a third party module to which you do not have access. Imagine 3 team working on 3 modules that has to work together but none of them exists. They can just define the communication channels and mock everything so that the code can be forced down on path otherwise inexistent yet.

      • Blacksonic

        design to interfaces not implementations, that solves the non existing classes problem

      • Patkos Csaba
        Author

        If your only point is to always find a counter example or alternative to whatever I say, you will succeed. There are countless ways you can do things in programming and many may be valid…

        And what if don’t have the interfaces yet because you are just in that step in TDD, before actually writing any code?

      • Blacksonic

        i was just pointing there are better practices for testing against non existing classes…i think it a bad practice thats all

        on the other way interfaces can solve so much problems

        if u have time visit Budapest Coderetreat and we can discuss it further in a pair programming session ;) https://www.facebook.com/events/512803602077101/

      • Patkos Csaba
        Author

        Thanks for the invitation, but I’ll be at the Timisoara Code Retreat event. As for your points, I consider them valid and right from a programming point of view. Your alternative solutions can be applied and I am using interfaces frequently. But I also suggest to you to be a little open and also consider how others are working. There is no absolute good or bad solution here. And since you are also a person frequenting code retreats, I presume you have the experience and knowledge to find use cases for my examples.

      • j_blotus

        When generating code at runtime in not sure there’s a better way. Does mockery not use eval() … eval() has uses in test code.

      • Blacksonic

        better mock or stub interfaces

        eval is evil, right?

      • j_blotus

        please let me know that works without eval()? I guess you could manually write the stub and mock classes but that removes most of the flexibility the mock generators offer. Eval is evil in production but again there are valid use cases

      • Blacksonic

        reflection, serialize it can be done with it…look into PHPUnit mock object generation from existing classes

  • http://www.facebook.com/jesus.bejarano.948 Jesus Bejarano

    Nettuts have made to like php more.

    • http://www.facebook.com/jesus.bejarano.948 Jesus Bejarano

      It will be a post about php modern best practices ?.

      • Patkos Csaba
        Author

        I don’t know, do you have something in mind you would like to see here? What kind of practices do you refer to? Maybe we could think of something.

  • http://twitter.com/GregorySalvan Grégory Salvan

    on fedora the package name is not “Mockery”.

    To intall it do:

    sudo yum install php-deepend-Mockery.noarch

    • Patkos Csaba
      Author

      Thanks for the clarification. I did not have a Fedora at hand and I just added what people recommended in various forums. I still think yum install Mockery should guess what you want even if you don’t give it the full package name… but still, I don’t have a Fedora to test it.

  • Gabriel Anderson

    How about Windows? I don’t understand because the tuts+ always ignore them. I think that is more easy to do few clicks to install anything. Otherwise you always offer ways to write several command lines in other systems even though Windows has about 84% of total users.

    • http://thorpesystems.com/ Tony R Quilkey

      Im not sure where you get these stats from or what your complaining about, but PHP should be developed on similar infrastructure to what it will be deployed on. Windows doesn’t even come into this picture.

      • David D’hont

        And what exactly gives you the right to decide on what platform PHP projects should be developed on?

      • Matthew

        He said “deployed on”.

        Have fun serving PHP from an IIS server.

      • David D’hont

        First of all, I asked a valid question in an attempt to prove a point, without being rude.

        Second, Congratulations on misinterpreting my reply. This childish behavior is exactly why I don’t usually post in comment sections. You reminded me that I should have ignored the comments.

        Third, If you want to be a proper developer, then your Arrogance and Bias are not going to get you far. How about you stop posting nonsense and start learning, or perhaps doing something that does not involve attacking people you don’t agree with?

      • http://thorpesystems.com/ Tony R Quilkey

        I don’t see any valid question. You replied to my post asking “What exactly gives you the right to dictate on what platform PHP projects should be developed on?” That is a completely invalid question considering I didn’t dictate anything in my original response. I was simply stating a fact.

      • David D’hont

        Gabriel and Jesus made a valid point. Nettuts only support unix like systems.
        Actual it’s more these Console Elitist mentality that does.

        It’s because I know the answer to the question that I replied the way I did. And that’s that most of these software was not developed with Windows in mind. And while that’s fine for a live webserver I see a lot of people here claiming they develop locally, and when Windows is your platform of choice then all of the sudden these console based “libraries”, “frameworks” and “packages” suddenly become useless.

        And don’t get me wrong, I don’t expect people to make things the way I want. But that does not mean we cannot ask “why not for us”?

        You said Windows doesn’t even come into this picture, yet I have been developing PHP websites on Windows XP, Vista and 7 for 10 years now, and I never had problems deploying to Linux servers.

        Oh yeah, wait, you assumed that I used a Windows server right? Oh well, I should just pack up and go, because… you stated a fact ladies and gentlemen.

      • Patkos Csaba
        Author

        I am simply entering this thread to point out that MacOS is also not a server OS… Yes, it is probably a best practice to develop on what you deploy but it is certainly not mandatory.

        David or Gabriel, if you feel the need, please tell everyone here how to install Mockery on Windows especially if there is anything more than what I’ve mentioned above. Thanks.

      • IanSimmons

        Composer has some real simple instructions for installing on Windows. http://getcomposer.org/doc/00-intro.md#installation-windows

        PEAR just sucks on any platform in my opinion but if it’s installed and working correctly the same instructions above should work regardless of platform. A while back I had a hard time installing phpunit (first time I used PEAR) and thought it was a problem with phpunit but after googling and fixing all the many issues I had with PEAR, phpunit installed and worked perfectly. I had the same issues on Ubuntu so I concluded that PEAR just sucks. :-)

        As for expecting you to provide special instructions, I think that is unfair. The majority of web developers seems to be Mac/Linux users because Mac/Linux are simply better development platforms. I wouldn’t expect a asp.net developer to provide special instructions for hacking visual studio to run on Mac/Linux.

    • http://www.facebook.com/jesus.bejarano.948 Jesus Bejarano

      Sorry but it seem that nettuts only support unix like systems.

    • Patkos Csaba
      Author

      It is usually up to the author to specify the ways to install things. The Composer and PEAR ways are universal, OS independent. I suppose there are just more authors on UNIX-like systems. Me personally didn’t use Windows for the past 10 or so years.

  • http://thorpesystems.com/ Tony R Quilkey

    Mockery is NOT an extension, it is a library written in PHP.

    • Patkos Csaba
      Author

      I used the term “extension” so that it is better understood what it is. From a purely technical point of view you are right.

      • http://thorpesystems.com/ Tony R Quilkey

        Calling it something it is not causes more confusion IMO, especially when there *is* such a thing as an extension in PHP.

  • Nathan

    Great post

  • Matthew

    You should reconsider your code samples. Not only are they very compressed and difficult to read, but you have needlessly polluted the code and extended its length by comparing Mockery to PHPUnit.

    If the premise of your article is to illustrate a better way than PHPUnit offers, then you can safely assume your readers are already proficient in PHPUnit and don’t need to reflect on the differences. And even if they are not, your article is sufficient enough to teach a developer how to begin using Mockery from scratch.

    You could have included a “PHPUnit vs Mockery” section to show the differences between use cases that was out of your sample “teaching” code.

    • Patkos Csaba
      Author

      So, you would have liked more an article about Mockery in general and only some highlights about the differences? I do not quite understand what you are suggesting.

      Also please elaborate why do you think the code examples were hard to understand so I can do better next time. Just saying “very compressed” and “difficult to read” doesn’t really help. Be sure I was trying to make it as easy to understand as I imagined it would need to be.

      So, shoot, and critique so I can do better next time.

      • Chris

        Agree with Matthew – the code samples are quite difficult to read, I think even just line breaks would help (ie. http://pastebin.com/f73EPjyC)

      • http://twitter.com/jtallant Justin Tallant

        First off the article was very helpful and I’m glad you wrote it. Some of the criticisms here could have been a bit more polite but are valid.

        For me the use of arbitrary class names and methods made the code harder to read. I think tutorials should avoid examples as arbitrary as “someClass, someObject, someMethod, Foo, Bar, etc.”. The lessons will be used in a real world use case so why not give a real world example?

        Start the lesson out with a goal, “We’re going to use use Mockery to mock the Instagram API”. People can take what they learned and apply it to some other class that needs mocking in their app. A real world use case goes a long way.

        And yes focusing more on just Mockery and not mixing PHPUnit mocks with Mockery mocks would have been helpful. Isolated examples of PHPUnit being more verbose would have been helpful. Thanks again for writing the article.

  • hasan

    Great Post. It would be a great help for the beginners. Thanks for the post

  • hasan

    Great Tutorial. This has certainly helped. Keep up the good work here.

  • shamim

    Very nice. Please do more. Easy to follow…Thanks

  • Chris

    In the ‘Return Values Based On A Given Parameter’ section, you’d have to create a callback function to recreate the functionality of getNumber() – http://pastebin.com/WUtHEcfj – agreed this isn’t exactly the same thing though, and I had to remove the with() calls.

  • David

    This tutorial could have been better if they have shown the usual way of testing it and then do it the mockery way.

    • Patkos Csaba
      Author

      Didn’t I just do that? I mean almost all of the examples contain a PHPUnit mock vs Mockery mock comparison…

  • http://twitter.com/alexborbely Alex Borbely

    ‘git clone git://github.com/padraic/Mockery.git’ does not work, use ‘git clone git://github.com/padraic/mockery.git’ instead

  • tommy
  • Abed Halawi

    This could be a misunderstanding from my side about mocking, and would really appreciate an explanation on how is mocking an inexistent or existent class is a good thing ? Say we have one class dependent on the output of the second class, and we change in the output format or whatever, wouldn’t this keep our tests succeeding although they’re outdated in the first class ?