How to mock an Object Factory - php

I use Factories (see http://www.php.net/manual/en/language.oop5.patterns.php for the pattern) a lot to increase the testability of our code. A simple factory could look like this:
class Factory
{
public function getInstanceFor($type)
{
switch ($type) {
case 'foo':
return new Foo();
case 'bar':
return new Bar();
}
}
}
Here is a sample class using that factory:
class Sample
{
protected $_factory;
public function __construct(Factory $factory)
{
$this->_factory = $factory;
}
public function doSomething()
{
$foo = $this->_factory->getInstanceFor('foo');
$bar = $this->_factory->getInstanceFor('bar');
/* more stuff done here */
/* ... */
}
}
Now for proper unit testing I need to mock the object that will return stubs for the classes, and that is where I got stuck. I thought it would be possible to do it like this:
class SampleTest extends PHPUnit_Framework_TestCase
{
public function testAClassUsingObjectFactory()
{
$fooStub = $this->getMock('Foo');
$barStub = $this->getMock('Bar');
$factoryMock = $this->getMock('Factory');
$factoryMock->expects($this->any())
->method('getInstanceFor')
->with('foo')
->will($this->returnValue($fooStub));
$factoryMock->expects($this->any())
->method('getInstanceFor')
->with('bar')
->will($this->returnValue($barStub));
}
}
But when I run the test, this is what I get:
F
Time: 0 seconds, Memory: 5.25Mb
There was 1 failure:
1) SampleTest::testDoSomething
Failed asserting that two strings are equal.
--- Expected
+++ Actual
## ##
-bar
+foo
FAILURES!
Tests: 1, Assertions: 0, Failures: 1.
So obviously it is not possible to let a mock object return different values depending on the passed method arguments this way.
How can this be done?

The problem is that the PHPUnit Mocking doesn't allow you to do this:
$factoryMock->expects($this->any())
->method('getInstanceFor')
->with('foo')
->will($this->returnValue($fooStub));
$factoryMock->expects($this->any())
->method('getInstanceFor')
->with('bar')
->will($this->returnValue($barStub));
You can only have one expects per ->method();. It is not aware of the fact that the parameters to ->with() differ!
So you just overwrite the first ->expects() with the second one. It's how those assertions are implemented and it's not what one would expect. But there are workarounds.
You need to define one expects with both behaviors / return values!
See: Mock in PHPUnit - multiple configuration of the same method with different arguments
When adapting the example to your problem it could look like this:
$fooStub = $this->getMock('Foo');
$barStub = $this->getMock('Bar');
$factoryMock->expects($this->exactly(2))
->method('getInstanceFor')
->with($this->logicalOr(
$this->equalTo('foo'),
$this->equalTo('bar')
))
->will($this->returnCallback(
function($param) use ($fooStub, $barStub) {
if($param == 'foo') return $fooStub;
return $barStub;
}
));

Create a simple stub factory class whose constructor takes the instances it should return.
class StubFactory extends Factory
{
private $items;
public function __construct(array $items)
{
$this->items = $items;
}
public function getInstanceFor($type)
{
if (!isset($this->items[$type])) {
throw new InvalidArgumentException("Object for $type not found.");
}
return $this->items[$type];
}
}
You can reuse this class in any unit test.
class SampleTest extends PHPUnit_Framework_TestCase
{
public function testAClassUsingObjectFactory()
{
$fooStub = $this->getMock('Foo');
$barStub = $this->getMock('Bar');
$factory = new StubFactory(array(
'foo' => $fooStub,
'bar' => $barStub,
));
...no need to set expectations on $factory...
}
}
For completeness, if you don't mind writing brittle tests, you can use at($index) instead of any() in your original code. This will break if the system under test changes the order or number of calls to the factory, but it's easy to write.
$factoryMock->expects($this->at(0))
->method('getInstanceFor')
->with('foo')
->will($this->returnValue($fooStub));
$factoryMock->expects($this->at(1))
->method('getInstanceFor')
->with('bar')
->will($this->returnValue($barStub));

you should change your "business logic" ... i mean you don't have to pass Factory to the Sample constructor, you have to pass the exact parameters you need

Related

Laravel Interface

Recently I came across interface from "Laravel 4 From Apprentice to Artisan" book with the example like this:
interface UserRepositoryInterface {
public function all();
}
class DbUserRepository implements UserRepositoryInterface {
public function all()
{
return User::all()->toArray();
}
}
What is interface? Where to put the interface file?
A Interface is a "contract" between itself and any class that implements the interface.
The contract states that any class that implements the interface should have all methods defined in the interface.
In this case DbUserRepository has to have a method named "all()" or a fatal error will occur when the class is instantiated.
The Interface file can be placed anywhere but the easiest is to put it in the same directory as the class that implements it.
The purpose of the interface is as follows:
Say you want to change your app from using a database (and Eloquent) and now instead you are going store data in JSON files and write your own methods for interacting with your JSON files. Now you can create a new repository e.g. JSONRepository and have it implement UserRepositoryInterface and because the interface forces you to define all the same methods that is defined in the interface, you can now be sure that your app will continue to work as it did. All this without you having to modify existing code.
The database example doesn't really make much real world sense to me because it is unlikely that I would change my storage system so drastically and the example always makes it seem like interfaces only have this one very small use case, which cant be further from the truth.
Coding to a interface has many benefits for you and your application.
Another example of interfaces in use can be:
Let's say you have a Calculator class and initially it has two operations it can perform (addition and multiplication). But a few weeks later you need to add another operation (e.g. subtraction), now normally this would mean you have to modify the calculator class and thus risk breaking it.
But if you are using a interface you can just create the Subtraction class and have it implement the CalculationInterface and now your app has a new operation without you touching existing code.
Example:
Calculator.php
<?php
class Calculator {
protected $result = null;
protected $numbers = [];
protected $calculation;
public function getResult()
{
return $this->result;
}
public function setNumbers()
{
$this->numbers = func_get_args();
}
public function setCalculation(CalculationInterface $calculation)
{
$this->calculation = $calculation;
}
public function calculate()
{
foreach ($this->numbers as $num)
{
$this->result = $this->calculation->run($num, $this->result);
}
return $this->result;
}
}
CalculationInterface.php
<?php
interface CalculationInterface {
public function run($num, $current);
}
Addition.php
<?php
class Addition implements CalculationInterface {
public function run($num, $current)
{
return $current + $num;
}
}
Multiplication.php
<?php
class Multiplication implements CalculationInterface {
public function run($num, $current)
{
/* if this is the first operation just return $num
so that we don't try to multiply $num with null */
if (is_null($current))
return $num;
return $current * $num;
}
}
Then to run the calculate method:
$this->calc = new Calculator;
$this->calc->setNumbers(5, 3, 7, 10);
$this->calc->setCalculation(new Addition);
$result = $this->calc->calculate(); //$result = 25
Now if you want to add a new operation let's say Subtraction you just create the Subtraction class and have it implement the CalculationInterface:
<?php
class Subtraction implements CalculationInterface {
public function run($num, $current)
{
/* if this is the first operation just return $num
so that we don't try to subtract from null */
if (is_null($current))
return $num;
return $current - $num;
}
}
Then to run it:
$this->calc = new Calculator;
$this->calc->setNumbers(30, 3, 7, 10);
$this->calc->setCalculation(new Subtraction);
$result = $this->calc->calculate(); //$result = 10
So in this example you are breaking your functionality up into smaller classes so that you can add, remove or even change them without breaking something else.

PHPUnit Mock Change the expectations later

I have a simple use case. I want to have a setUp method which will cause my mock object to return a default value:
$this->myservice
->expects($this->any())
->method('checkUniqueness')
->will($this->returnValue(true));
But then in some tests, I want to return a different value:
$this->myservice
->expects($this->exactly(1))
->method('checkUniqueness')
->will($this->returnValue(false));
I've used GoogleMock for C++ in the past and it had "returnByDefault" or something to handle that. I couldn't figure out if this is possible in PHPUnit (there is no api documentation and the code is difficult to read through to find what I want).
Now I can't just change $this->myservice to a new mock, because in setup, I pass it into other things that need to be mocked or tested.
My only other solution is that I lose the benefit of the setup and instead have to build up all of my mocks for every test.
You could move the setUp() code into another method, which has parameters. This method gets then called from setUp(), and you may call it also from your test method, but with parameters different to the default ones.
Continue building the mock in setUp() but set the expectation separately in each test:
class FooTest extends PHPUnit_Framework_TestCase {
private $myservice;
private $foo;
public function setUp(){
$this->myService = $this->getMockBuilder('myservice')->getMock();
$this->foo = new Foo($this->myService);
}
public function testUniqueThing(){
$this->myservice
->expects($this->any())
->method('checkUniqueness')
->will($this->returnValue(true));
$this->assertEqual('baz', $this->foo->calculateTheThing());
}
public function testNonUniqueThing(){
$this->myservice
->expects($this->any())
->method('checkUniqueness')
->will($this->returnValue(false));
$this->assertEqual('bar', $this->foo->calculateTheThing());
}
}
The two expectations will not interfere with each other because PHPUnit instantiates a new instance of FooTest to run each test.
Another little trick is to pass the variable by reference. That way you can manipulate the value:
public function callApi(string $endpoint):bool
{
// some logic ...
}
public function getCurlInfo():array
{
// returns curl info about the last request
}
The above code has 2 public methods: callApi() that calls the API, and a second getCurlInfo()-method that provides information about the last request that's been done. We can mock the output of getCurlInfo() according to the arguments provided / mocked for callApi() by passing a variable as reference:
$mockedHttpCode = 0;
$this->mockedApi
->method('callApi')
->will(
// pass variable by reference:
$this->returnCallback(function () use (&$mockedHttpCode) {
$args = func_get_args();
$maps = [
['endpoint/x', true, 200],
['endpoint/y', false, 404],
['endpoint/z', false, 403],
];
foreach ($maps as $map) {
if ($args == array_slice($map, 0, count($args))) {
// change variable:
$mockedHttpCode = $map[count($args) + 1];
return $map[count($args)];
}
}
return [];
})
);
$this->mockedApi
->method('getCurlInfo')
// pass variable by reference:
->willReturn(['http_code' => &$mockedHttpCode]);
If you look closely, the returnCallback()-logic actually does the same thing as returnValueMap(), only in our case we can add a 3rd argument: the expected response code from the server.

PropelORM, Symfony 2 and Unit testing

I'm used to the habit of writing like this:
$results = SomeModelQuery::create()->filterByFoo('bar')->find();
However this does not scale for unit testing because I can't inject a mock object, i.e. I can't affect what data is returned. I'd like to use fixture data, but I can't.
Nor does it seem great to inject an object:
class Foo
{
public __construct($someModelQuery)
{
$this->someModelQuery = $someMOdelQuery;
}
public function doSthing()
{
$results = $this->someModelQuery->filterByFoo('bar')->find();
}
}
DI feels horrible. I have tens of query objects to mock and throw. Setting through constructor is ugly and painful. Setting using method is wrong because it can be forgotten when calling. And it feels painful to always for every single lib and action to create these query objects manually.
How would I elegantly do DI with PropelORM query classes? I don't want to call a method like:
$oneQuery = OneQuery::create();
$anotherQuery = AnotherQuery::create();
// ... 10 more ...
$foo = new Foo($oneQuery, $anotherQuery, ...);
$foo->callSomeFunctionThatNeedsThose();
In my opinion (and Martin Folowers's) there is a step between calling everything statically and using Dependency Injection and it may be what you are looking for.
Where I can't do full DI (Zend Framework MVC for example) I will use a Service Locator. A Service Layer will be the place that all your classes go to get there dependencies from. Think of it as a one layer deep abstraction for your classes dependencies. There are many benefits to using a Service Locator but I will focus on testability in this case.
Let's get into some code, here is are model query class
class SomeModelQuery
{
public function __call($method, $params)
{
if ($method == 'find') {
return 'Real Data';
}
return $this;
}
}
All it does is return itself unless the method 'find' is called. Then is will return the hard-coded string "Real Data".
Now our service locator:
class ServiceLocator
{
protected static $instance;
protected $someModelQuery;
public static function resetInstance()
{
static::$instance = null;
}
public static function instance()
{
if (self::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
public function getSomeModelQuery()
{
if ($this->someModelQuery === null) {
$this->someModelQuery = new SomeModelQuery();
}
return $this->someModelQuery;
}
public function setSomeModelQuery($someModelQuery)
{
$this->someModelQuery = $someModelQuery;
}
}
This does two things. Provides a global scope method instance so you can always get at it. Along with allowing it to be reset. Then providing get and set methods for the model query object. With lazy loading if it has not already been set.
Now the code that does the real work:
class Foo
{
public function doSomething()
{
return ServiceLocator::instance()
->getSomeModelQuery()->filterByFoo('bar')->find();
}
}
Foo calls the service locator, it then gets an instance of the query object from it and does the call it needs to on that query object.
So now we need to write some unit tests for all of this. Here it is:
class FooTest extends PHPUnit_Framework_TestCase
{
protected function setUp()
{
ServiceLocator::resetInstance();
}
public function testNoMocking()
{
$foo = new Foo();
$this->assertEquals('Real Data', $foo->doSomething());
}
public function testWithMock()
{
// Create our mock with a random value
$rand = mt_rand();
$mock = $this->getMock('SomeModelQuery');
$mock->expects($this->any())
->method('__call')
->will($this->onConsecutiveCalls($mock, $rand));
// Place the mock in the service locator
ServiceLocator::instance()->setSomeModelQuery($mock);
// Do we get our random value back?
$foo = new Foo();
$this->assertEquals($rand, $foo->doSomething());
}
}
I've given an example where the real query code is called and where the query code is mocked.
So this gives you the ability to inject mocks with out needing to inject every dependency into the classes you want to unit test.
There are many ways to write the above code. Use it as a proof of concept and adapt it to your need.

PHPUnit Test How Many Times A Function Is Called

I'm working on a test in phpunit and I'm running into an issue. I have a public function on my class that I am trying to test. Depending on the parameters passed in to the method, a protected function also in my test class will be called one or two times. I currently have a test in place to check that the return data is correct, but I would also like to make sure the protected method is being called the correct number of times.
I know that a mock object will allow me to count the number of times a function is called, but it will also override the value returned by the protected function. I tried using a mock object with no "will" section, but it would just return null, not the actual value for the protected method.
ExampleClass
public function do_stuff($runTwice){
$results = do_cool_stuff();
if($runTwice){
$results = 2 * do_cool_stuff();
}
return $results;
}
protected function do_cool_stuff()
{
return 2;
}
In my test, I want to check whether do_cool_stuff() was called once or twice, but I still want the return values of both functions to be the same so I can test those as well in my unit test.
tl;dr
I want to count the number of times a protected method in my test object is called (like you can do with a mock object) but I still want all the methods in my test method to return their normal values (not like a mock object).
Alternatively, revert back to rolling your own testable stand-in. The following aint pretty, but you get the idea:
class ExampleClass {
public function do_stuff($runTwice) {
$results = $this->do_cool_stuff();
if ($runTwice) {
$results = 2 * $this->do_cool_stuff();
}
return $results;
}
protected function do_cool_stuff() {
return 2;
}
}
class TestableExampleClass extends ExampleClass {
/** Stores how many times the do_cool_stuff method is called */
protected $callCount;
function __construct() {
$this->callCount = 0;
}
function getCallCount() {
return $this->callCount;
}
/** Increment the call counter, and then use the base class's functionality */
protected function do_cool_stuff() {
$this->callCount++;
return parent::do_cool_stuff();
}
}
class ExampleClassTest extends PHPUnit_Framework_TestCase {
public function test_do_stuff() {
$example = new ExampleClass();
$this->assertEquals(2, $example->do_stuff(false));
$this->assertEquals(4, $example->do_stuff(true));
}
public function test_do_cool_stuff_is_called_correctly() {
// Try it out the first way
$firstExample = new TestableExampleClass();
$this->assertEquals(0, $firstExample->getCallCount());
$firstExample->do_stuff(false);
$this->assertEquals(1, $firstExample->getCallCount());
// Now test the other code path
$secondExample = new TestableExampleClass();
$this->assertEquals(0, $secondExample->getCallCount());
$secondExample->do_stuff(true);
$this->assertEquals(2, $secondExample->getCallCount());
}
}
I wonder though whether counting the number of times a protected method has been called is really a good test. It's coupling your test to the implementation pretty hard. Does it really matter whether it is called twice, or are you more interested in the interactions with other objects? Or maybe this is pointing towards do_cool_stuff needing a refactor into two separate methods:
class ExampleClass {
public function do_stuff($runTwice) {
if ($runTwice) {
return $this->do_cool_stuff_twice();
} else {
return $this->do_cool_stuff_once();
}
}
//...
}
Try setting a global variable prior to utilizing the class.
$IAmDeclaredOutsideOfTheFunction;
then use it to store the count and simply check it after your functions and classes have been called.

unit testing datastores in PHP

I'm using PHPUnit but find it difficult to make it create good mocks and stubs for objects used as datastore.
Example:
class urlDisplayer {
private $storage;
public function __construct(IUrlStorage $storage) { $this->storage = $storage; }
public function displayUrl($name) {}
public function displayLatestUrls($count) {}
}
interface IUrlStorage {
public function addUrl($name, $url);
public function getUrl($name);
}
class MysqlUrlStorage implements IUrlStorage {
// saves and retrieves from database
}
class NonPersistentStorage implements IUrlStorage {
// just stores for this request
}
Eg how to have PHPUnit stubs returning more than one possible value on two calls with different $names?
Edit: example test:
public function testUrlDisplayerDisplaysLatestUrls {
// get mock storage and have it return latest x urls so I can test whether
// UrlDisplayer really shows the latest x
}
In this test the mock should return a number of urls, however in the documentation I only how to return one value.
Your question is not very clear - but I assume you are asking how to use phpunit's mock objects to return a different value in different situations?
PHPUnit's mock classes allow you specify a custom function (ie: a callback function/method) - which is practically unlimited in what it can do.
In the below example, I created a mock IUrlStorage class that will return the next url in its storage each time it is called.
public function setUp()
{
parent::setUp();
$this->fixture = new UrlDisplayer(); //change this to however you create your object
//Create a list of expected URLs for testing across all test cases
$this->expectedUrls = array(
'key1' => 'http://www.example.com/url1/'
, 'key2' => 'http://www.example.net/url2/'
, 'key3' => 'http://www.example.com/url3/'
);
}
public function testUrlDisplayerDisplaysLatestUrls {
//Init
$mockStorage = $this->getMock('IUrlStorage');
$mockStorage->expects($this->any())
->method('getUrl')
->will( $this->returnCallback(array($this, 'mockgetUrl')) );
reset($this->expectedUrls); //reset array before testing
//Actual Tests
$this->assertGreaterThan(0, count($this->expectedUrls));
foreach ( $this->expectedUrls as $key => $expected ) {
$actual = $this->fixture->displayUrl($key);
$this->assertEquals($expected, $actual);
}
}
public function mockGetUrl($name)
{
$value = current($this->expectedUrls);
next($this->expectedUrls);
//Return null instead of false when end of array is reached
return ($value === false) ? null : $value;
}
Alternatively, sometimes it is easier to simply create a real class that mocks up the necessary functionality. This is especially easy with well defined and small interfaces.
In this specific case, I would suggest using the below class instead:
class MockStorage implements IUrlStorage
{
protected $urls = array();
public function addUrl($name, $url)
{
$this->urls[$name] = $url;
}
public function getUrl($name)
{
if ( isset($this->urls[$name]) ) {
return $this->urls[$name];
}
return null;
}
}
?>
Then in your unit test class you simply instantiate your fixture like below:
public function setUp() {
$mockStorage = new MockStorage();
//Add as many expected URLs you want to test for
$mockStorage->addUrl('name1', 'http://example.com');
//etc...
$this->fixture = new UrlDisplayer($mockStorage);
}

Categories