Mock in PHPUnit - multiple configuration of the same method with different arguments - php

Is it possible to configure PHPUnit mock in this way?
$context = $this->getMockBuilder('Context')
->getMock();
$context->expects($this->any())
->method('offsetGet')
->with('Matcher')
->will($this->returnValue(new Matcher()));
$context->expects($this->any())
->method('offsetGet')
->with('Logger')
->will($this->returnValue(new Logger()));
I use PHPUnit 3.5.10 and it fails when I ask for Matcher because it expects "Logger" argument.
It is like the second expectation is rewriting the first one, but when I dump the mock, everything looks ok.

Sadly this is not possible with the default PHPUnit Mock API.
I can see two options that can get you close to something like this:
Using ->at($x)
$context = $this->getMockBuilder('Context')
->getMock();
$context->expects($this->at(0))
->method('offsetGet')
->with('Matcher')
->will($this->returnValue(new Matcher()));
$context->expects($this->at(1))
->method('offsetGet')
->with('Logger')
->will($this->returnValue(new Logger()));
This will work fine but you are testing more than you should (mainly that it gets called with matcher first, and that is an implementation detail).
Also this will fail if you have more than one call to each of of the functions!
Accepting both parameters and using returnCallBack
This is more work but works nicer since you don't depend on the order of the calls:
Working example:
<?php
class FooTest extends PHPUnit_Framework_TestCase {
public function testX() {
$context = $this->getMockBuilder('Context')
->getMock();
$context->expects($this->exactly(2))
->method('offsetGet')
->with($this->logicalOr(
$this->equalTo('Matcher'),
$this->equalTo('Logger')
))
->will($this->returnCallback(
function($param) {
var_dump(func_get_args());
// The first arg will be Matcher or Logger
// so something like "return new $param" should work here
}
));
$context->offsetGet("Matcher");
$context->offsetGet("Logger");
}
}
class Context {
public function offsetGet() { echo "org"; }
}
This will output:
/*
$ phpunit footest.php
PHPUnit 3.5.11 by Sebastian Bergmann.
array(1) {
[0]=>
string(7) "Matcher"
}
array(1) {
[0]=>
string(6) "Logger"
}
.
Time: 0 seconds, Memory: 3.00Mb
OK (1 test, 1 assertion)
I've used $this->exactly(2) in the matcher to show that this does also work with counting the invocations. If you don't need that swapping it out for $this->any() will, of course, work.

As of PHPUnit 3.6, there is $this->returnValueMap() which may be used to return different values depending on the given parameters to the method stub.

You can achieve this with a callback:
class MockTest extends PHPUnit_Framework_TestCase
{
/**
* #dataProvider provideExpectedInstance
*/
public function testMockReturnsInstance($expectedInstance)
{
$context = $this->getMock('Context');
$context->expects($this->any())
->method('offsetGet')
// Accept any of "Matcher" or "Logger" for first argument
->with($this->logicalOr(
$this->equalTo('Matcher'),
$this->equalTo('Logger')
))
// Return what was passed to offsetGet as a new instance
->will($this->returnCallback(
function($arg1) {
return new $arg1;
}
));
$this->assertInstanceOf(
$expectedInstance,
$context->offsetGet($expectedInstance)
);
}
public function provideExpectedInstance()
{
return array_chunk(array('Matcher', 'Logger'), 1);
}
}
Should pass for any "Logger" or "Matcher" arguments passed to the Context Mock's offsetGet method:
F:\Work\code\gordon\sandbox>phpunit NewFileTest.php
PHPUnit 3.5.13 by Sebastian Bergmann.
..
Time: 0 seconds, Memory: 3.25Mb
OK (2 tests, 4 assertions)
As you can see, PHPUnit ran two tests. One for each dataProvider value. And in each of those tests it made the assertion for with() and the one for instanceOf, hence four assertions.

Following on from the answer of #edorian and the comments (#MarijnHuizendveld) regarding ensuring that the method is called with both Matcher and Logger, and not simply twice with either Matcher or Logger, here is an example.
$expectedArguments = array('Matcher', 'Logger');
$context->expects($this->exactly(2))
->method('offsetGet')
->with($this->logicalOr(
$this->equalTo('Matcher'),
$this->equalTo('Logger')
))
->will($this->returnCallback(
function($param) use (&$expectedArguments){
if(($key = array_search($param, $expectedArguments)) !== false) {
// remove called argument from list
unset($expectedArguments[$key]);
}
// The first arg will be Matcher or Logger
// so something like "return new $param" should work here
}
));
// perform actions...
// check all arguments removed
$this->assertEquals(array(), $expectedArguments, 'Method offsetGet not called with all required arguments');
This is with PHPUnit 3.7.
If the method you are testing doesn't actually return anything, and you simply need to test that it is called with the correct arguments, the same approach applies. For this scenario, I also attempted doing this using a callback function for $this->callback as the argument to the with, rather than returnCallback in the will. This fails, as internally phpunit calls the callback twice in the process of verifying the argument matcher callback. This means that the approach fails as on the second call that argument has already been removed from the expected arguments array. I don't know why phpunit calls it twice (seems an unnecessary waste), and I guess you could work around that by only removing it on the second call, but I wasn't confident enough that this is intended and consistent phpunit behaviour to rely on that occurring.

My 2 cents to the topic: pay attention when using at($x): it means that expected method call will be the ($x+1)th method call on the mock object; it doesn't mean that will be the ($x+1)th call of the expected method. This made me waste some time so I hope it won't with you. Kind regards to everyone.

I just stumbled on this PHP extension to mock objects: https://github.com/etsy/phpunit-extensions/wiki/Mock-Object

Here are also some solutions with the doublit library :
Solution 1 : using Stubs::returnValueMap
/* Get a dummy double instance */
$double = Doublit::dummy_instance(Context::class);
/* Test the "offsetGet" method */
$double::_method('offsetGet')
// Test that the first argument is equal to "Matcher" or "Logger"
->args([Constraints::logicalOr('Matcher', 'Logger')])
// Return "new Matcher()" when first argument is "Matcher"
// Return "new Logger()" when first argument is "Logger"
->stub(Stubs::returnValueMap([['Matcher'], ['Logger']], [new Matcher(), new Logger()]));
Solution 2 : using a callback
/* Get a dummy double instance */
$double = Doublit::dummy_instance(Context::class);
/* Test the "offsetGet" method */
$double::_method('offsetGet')
// Test that the first argument is equal to "Matcher" or "Logger"
->args([Constraints::logicalOr('Matcher', 'Logger')])
// Return "new Matcher()" when first argument $arg is "Matcher"
// Return "new Logger()" when first argument $arg is "Logger"
->stub(function($arg){
if($arg == 'Matcher'){
return new Matcher();
} else if($arg == 'Logger'){
return new Logger();
}
});

Related

PHPUnit Test result type or also the result variables

during unit testing i'm always get confused about what to test.
Do i need to test the API and only the API or also the method result values.
class SomeEventHandler
{
public function onDispatch (Event $event)
{
if ($event->hasFoo)
{
$model = $this->createResponseModel('foo');
}
else
{
$model = $this->createResponseModel('bar');
}
// End.
return $model;
}
private function createResponseModel ($foo)
{
$vars = array(
'someVare' => true,
'foo' => $foo
);
// End.
return new ResponseModel($vars);
}
}
So should i test if the method onDispatch returns a instance of ResponseModel or should i also test if the variable foo is set properly?
Or is the test below just fine?
class SomeEventHandlerTest
{
// assume that a instance of SomeEventHandler is created
private $someEventHandler;
public function testOnDispatch_EventHasFoo_ReturnsResponseModel ()
{
$e = new Event();
$e->hasFoo = true;
$result = $someEventHandler->onDispatch($e);
$this->assertInstanceOf('ResponseModel', $result);
}
public function testOnDispatch_EventHasNoFoo_ReturnsResponseModel ()
{
$e = new Event();
$e->hasFoo = false;
$result = $someEventHandler->onDispatch($e);
$this->assertInstanceOf('ResponseModel', $result);
}
}
If you were checking the code by hand what is it that you would check? Just that a ResponseModel was returned or that it also had the proper values?
If you weren't writing tests and executed the code what would you look for to ensure that the code was doing what it was supposed to. You would check that the values in the returned object were correct. I would do that by using the public API of the object and verify that the values are right.
One idea is to have the tests such that if the code were deleted, you would be able to recreate all the functionality via only having the tests. Only checking the returned object could result in a function that just has return new ResponseModel();. This would pass the test but would not be what you want.
In short, what you decide to test is subjective, however you should at the minimum test all your public methods.
Many people limit their tests to public methods and simply ensure code coverage on the protected/private methods is adequate. However, feel free to test anything you think warrants a test. Generally speaking, the more tests the better.
In my opinion you should certainly test for your response data, not just the return type.
I rely on Unit Tests to let me make code changes in the future and be satisfied my changes have not created any breaks, just by running the tests.
So in your case, if the "foo" or "bar" response data is important, you should test it.
That way if you later change the response strings by accident, your tests will tell you.

Build a UnitTest with multiple options on the same mocked-method

I have a problem to test a Method like that
public function index(){
if($this->request->is('get')){
if($this->Session->check('saveConflict')){
$this->set('conflict',true);
}else{
$this->set('data','test');
}
if($this->Session->check('full')){
$this->set('data',$this->Model->find('all'));
}else{
$this->set('data','test');
}
}else{
throw new BadRequestException;
}
}
unless that method maybe doesn't make sense, here is my problem. I have to call the method "check" on the Session-Component twice. But I want that for example the first methode mock-call retruns a "false" and the second a "true".
Here's what I have
$this->Editors->Session
->expects($this->once())
->method('check')
->will($this->returnValue(true));
I've tried it with the expectation "$this->at(1)" the call via order-index. But i think that isnt pretty clever because if I add a Session->check anywhere in the interpreted way though my base-method for example i have to change all those test-lines to make it work properly again.
I use CakePHP 2.4.6 and php-unit 4.1.3.
Is there any other why to do what I want to do?
Use the
->will($this->onConsecutiveCalls(array('return_value1',
'retur_value2',
...)));
Give the sequence of the return value in the order you want, the mocked method will return it in order.Or you can try the $this->returnCallback to do some sophisticated customize.You can find the example here How can I get PHPUnit MockObjects to return differernt values based on a parameter?.
Example
If you just want to do the unit test and cover all the path,I'll do like this:
public function testIndex()
{
....
$this->Editors->Session
->expects($this->any())
->method('check')
->will($this->returnCallback(array($this,'sessionCallback')));
$this->object->index();
$this->object->index();
.....
}
private $sessionFlag;
public function sessionCallback($value)
{
$rtnValue = $this->sessionFlag[$value];
$this->sessionFlag[$value] = (!$rtnValue);
return $rtnValue;
}

understanding pimple php source code

Pimple is a simple dependency injection container in php used in silex framework. I was going through the source code here. In the documentation the function offsetGet returns the same instance of the class that is attached to the dependency container. the relevant code for offsetGet is :
public function offsetGet($id)
{
if (!isset($this->keys[$id])) {
throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
}
if (
isset($this->raw[$id])
|| !is_object($this->values[$id])
|| isset($this->protected[$this->values[$id]])
|| !method_exists($this->values[$id], '__invoke')
) {
return $this->values[$id];
}
if (isset($this->factories[$this->values[$id]])) {
return $this->values[$id]($this);
}
$this->frozen[$id] = true;
$this->raw[$id] = $this->values[$id];
return $this->values[$id] = $this->values[$id]($this);
}
Here, if the object is in the factories Object Store(SplObjectStorage type), it returns a new instance of the class with id $id. then in the last return again $this->values[$id] is set to a new instance of the object and that new instance is returned.
return $this->values[$id] = $this->values[$id]($this).
This is the line I fail to understand. How is this line supposed to return the same instance for different calls of offsetGet for the same $id. Won't it return a new instance every time?
Please help me. I tried a lot but I don't get it.
I looked at the source code of pimple and found out that once the object is instantiated and kept in $this->values[$id], the next call of offsetGet will return from the second if condition.
i.e this if condition:
if (
isset($this->raw[$id])
|| !is_object($this->values[$id])
|| isset($this->protected[$this->values[$id]])
|| !method_exists($this->values[$id], '__invoke')
) {
return $this->values[$id];
}
Looking at the unit tests, i found out that the objects without the magic method __invoke can be shared . If the object has a magic method __invoke(i.e the object can be treated as a function) , a new instance is returned everytime.
So, you can see that the first, second and third condition on the above if statement return false . but the fourth condition returns true and hence the $this->values[$id] returns the same instance every time.

How do you mock a virtual binary file so that exec() / system() / passthru() function output can be tested?

I have an interesting problem and have searched the internet, but haven't yet found an answer.
I work for a company that doesn't allow it's workers to utilize OOP, it is kind of ridiculous, but the working experience is valuable.
Consider the following function:
function get_setting_values_from_file( $parameter )
{
exec("/usr/var/binary --options $parameter", $output, $return);
$settings = file( $output[0] );
foreach( $settings as $setting ) {
if( strstr( $setting, "color") ) {
$setting = explode( ":", $setting );
return $setting[1];
}
}
return false;
}
I need to unit test a similar function. I am currently using phpUnit for my tests and the vfsStream libraries to mock the file system, but how do you mock the call to exec("/usr/var/binary --options $parameter", $output, $return) when I'm developing with no access to the actual system? What is the recommend approach for dealing with test cases like this?
All feedback is appreciated.
You could mock exec() by using a function mock library. I made one (php-mock) for you which requires you to use namespaces
namespace foo;
use phpmock\phpunit\PHPMock;
class ExecTest extends \PHPUnit_Framework_TestCase
{
use PHPMock;
public function testExec()
{
$mock = $this->getFunctionMock(__NAMESPACE__, "exec");
$mock->expects($this->once())->willReturnCallback(
function ($command, &$output, &$return_var) {
$this->assertEquals("foo", $command);
$output = "failure";
$return_var = 1;
}
);
exec("foo", $output, $return_var);
$this->assertEquals("failure", $output);
$this->assertEquals(1, $return_var);
}
}
Simply mock this function to return the text that you are trying to get into $settings. You do not need to call the executable, simply create the file or return.
For instance, assuming the function get_setting_values_from_file() returns the settings as an array, you can simply mock the function in your test to return the settings as an array. Create a test stub to mock the object that contains the get_setting_values_from_file() method, and have that mock simply return the same FALSE, 1 or 2 that the test assumed.
$stub = $this->getMock('GetSettingsClass');
$stub->expects($this->any())
->method('get_settings_from_file')
->will($this->returnValue(0));
This is from the PHPUnit manual -> http://phpunit.de/manual/3.8/en/test-doubles.html#test-doubles.stubs
Optionally, you could even bypass the call, and simply test the functions/code that works on the returns by creating the array and passing it to those functions.
Assumed Example in the main code:
...
$settings = get_setting_values_from_file( 'UserType' );
$UserType = get_user_type($settings);
return $UserType;
function get_user_type($settings)
{
if($settings !== FALSE) // Returned from your function if parameter is not found
{
switch($settings)
{
case 1:
return 'User'; // Best to use Constants, but for example here only
break;
case 2:
return 'Admin';
break;
...
}
}
else
{
return FALSE;
}
}
Now, in your test, you can simply
$this->assertFalse(get_user_type(FALSE, 'Ensure not found data is handled properly as FALSE is returned');
$this->assertEqual('User', get_user_type(1), 'Test UserType=1');
$this->assertEqual('Admin', get_user_type(1), 'Test UserType=2');
...
These work as the code does not call the function that had to mock the read from the OS, but does handle all the expected returns by calling the function processing the setting return value. Here, you have simply assumed the return from the function 'get_setting_values_from_file()' without needing the file or any mocks.
This does NOT however test reading from the file, which I would do in another test by using the setUp and tearDown to actual create a file with the values you want (fopen/fwrite) and then call your function and ensure it returns what is expected.
I hope this helps to explain what I was thinking.

Fast check if an object will be successfully instantiated in PHP?

How can I check if an object will be successfully instantiated with the given argument, without actually creating the instance?
Actually I'm only checking (didn't tested this code, but should work fine...) the number of required parameters, ignoring types:
// Filter definition and arguments as per configuration
$filter = $container->getDefinition($serviceId);
$args = $activeFilters[$filterName];
// Check number of required arguments vs arguments in config
$constructor = $reflector->getConstructor();
$numRequired = $constructor->getNumberOfRequiredParameters();
$numSpecified = is_array($args) ? count($args) : 1;
if($numRequired < $numSpecified) {
throw new InvalidFilterDefinitionException(
$serviceId,
$numRequired,
$numSpecified
);
}
EDIT: $constructor can be null...
The short answer is that you simply cannot determine if a set of arguments will allow error-free instantiation of a constructor. As commenters have mentioned above, there's no way to know for sure if a class can be instantiated with a given argument list because there are runtime considerations that cannot be known without actually attempting
instantiation.
However, there is value in trying to instantiate a class from a list of constructor arguments. The most obvious use-case for this sort of operation is a configurable Dependency Injection Container (DIC). Unfortunately, this is a much more complicated operation than the OP suggests.
We need to determine for each argument in a supplied definition array whether or not it matches specified type-hints from the constructor method signature (if the method signature actually has type-hints). Also, we need to resolve how to treat default argument values. Additionally, for our code to be of any real use we need to allow the specification of "definitions" ahead of time for instantiating a class. A sophisticated treatment of the problem will also involve a pool of reflection objects (caching) to minimize the performance impact of repeatedly reflecting things.
Another hurdle is the fact that there's no way to access the type-hint of a reflected method parameter without calling its ReflectionParameter::getClass method and subsequently instantiating a reflection class from the returned class name (if null is returned the param has no type-hint). This is where caching generated reflections becomes particularly important for any real-world use-case.
The code below is a severely stripped-down version of my own string-based recursive dependency injection container. It's a mixture of pseudo-code and real-code (if you were hoping for free code to copy/paste you're out of luck). You'll see that the code below matches the associative array keys of "definition" arrays to the parameter names in the constructor signature.
The real code can be found over at the relevant github project page.
class Provider {
private $definitions;
public function define($class, array $definition) {
$class = strtolower($class);
$this->definitions[$class] = $definition;
}
public function make($class, array $definition = null) {
$class = strtolower($class);
if (is_null($definition) && isset($this->definitions[$class])) {
$definition = $this->definitions[$class];
}
$reflClass = new ReflectionClass($class);
$instanceArgs = $this->buildNewInstanceArgs($reflClass);
return $reflClass->newInstanceArgs($instanceArgs);
}
private function buildNewInstanceArgs(
ReflectionClass $reflClass,
array $definition
) {
$instanceArgs = array();
$reflCtor = $reflClass->getConstructor();
// IF no constructor exists we're done and should just
// return a new instance of $class:
// return $this->make($reflClass->name);
// otherwise ...
$reflCtorParams = $reflCtor->getParameters();
foreach ($reflCtorParams as $ctorParam) {
if (isset($definition[$ctorParam->name])) {
$instanceArgs[] = $this->make($definition[$ctorParam->name]);
continue;
}
$typeHint = $this->getParameterTypeHint($ctorParam);
if ($typeHint && $this->isInstantiable($typeHint)) {
// The typehint is instantiable, go ahead and make a new
// instance of it
$instanceArgs[] = $this->make($typeHint);
} elseif ($typeHint) {
// The typehint is abstract or an interface. We can't
// proceed because we already know we don't have a
// definition telling us which class to instantiate
throw Exception;
} elseif ($ctorParam->isDefaultValueAvailable()) {
// No typehint, try to use the default parameter value
$instanceArgs[] = $ctorParam->getDefaultValue();
} else {
// If all else fails, try passing in a NULL or something
$instanceArgs[] = NULL;
}
}
return $instanceArgs;
}
private function getParameterTypeHint(ReflectionParameter $param) {
// ... see the note about retrieving parameter typehints
// in the exposition ...
}
private function isInstantiable($class) {
// determine if the class typehint is abstract/interface
// RTM on reflection for how to do this
}
}

Categories