PHPUnit: how do I mock multiple method calls with multiple arguments? - php

I am writing a unit test for a method using PHPUnit. The method I am testing makes a call to the same method on the same object 3 times but with different sets of arguments. My question is similar to the questions asked here and here
The questions asked in the other posts have to do with mocking methods that only take one argument.
However, my method takes multiple arguments and I need something like this:
$mock->expects($this->exactly(3))
->method('MyMockedMethod')
->with(
$this->logicalOr(
$this->equalTo($arg1, $arg2, arg3....argNb),
$this->equalTo($arg1b, $arg2b, arg3b....argNb),
$this->equalTo($arg1c, $arg2c, arg3c....argNc)
)
);
This code doesn't work because equalTo() validates only one argument. Giving it more than one argument throws an exception:
Argument #2 of PHPUnit_Framework_Constraint_IsEqual::__construct() must be a numeric
Is there a way to do a logicalOr mocking for a method with more than one argument?

In my case the answer turned out to be quite simple:
$this->expects($this->at(0))
->method('write')
->with(/* first set of params */);
$this->expects($this->at(1))
->method('write')
->with(/* second set of params */);
The key is to use $this->at(n), with n being the Nth call of the method. I couldn't do anything with any of the logicalOr() variants I tried.

For others who are looking to both match input parameters and provide return values for multiple calls.. this works for me:
$mock->method('myMockedMethod')
->withConsecutive([$argA1, $argA2], [$argB1, $argB2], [$argC1, $argC2])
->willReturnOnConsecutiveCalls($retValue1, $retValue2, $retValue3);

Stubbing a method call to return the value from a map
$map = array(
array('arg1_1', 'arg2_1', 'arg3_1', 'return_1'),
array('arg1_2', 'arg2_2', 'arg3_2', 'return_2'),
array('arg1_3', 'arg2_3', 'arg3_3', 'return_3'),
);
$mock->expects($this->exactly(3))
->method('MyMockedMethod')
->will($this->returnValueMap($map));
Or you can use
$mock->expects($this->exactly(3))
->method('MyMockedMethod')
->will($this->onConsecutiveCalls('return_1', 'return_2', 'return_3'));
if you don't need to specify input arguments

In case someone finds this without looking at the correspondent section in the phpunit documentation, you can use the withConsecutive method
$mock->expects($this->exactly(3))
->method('MyMockedMethod')
->withConsecutive(
[$arg1, $arg2, $arg3....$argNb],
[arg1b, $arg2b, $arg3b....$argNb],
[$arg1c, $arg2c, $arg3c....$argNc]
...
);
The only downside of this being that the code MUST call the MyMockedMethod in the order of arguments supplied. I have not yet found a way around this.

Related

Call Symfony Repository method with parameters from array

symfony 3.4
I have to call a function
$this->em->getRepository($info['repoName'])->$info['funcName']->(//params
the problem is that my function has parameters by coma like
functionName($param1, $param2);
and I receive an array that has values of this parameters in
$arr['params']
VERY IMPORTANT!
I will not be able to call it like functionName($arr['params'][0], $arr['params'][1]) because I will never know what function from what repo is used and how many params it has, this all info is stored in yml, taken from there, data is just found in other functions and just passed here. Guys please pay attention at this - this is dynamic, there may be any number of params in array and it is of course the same as in function, but how to call the function when parameters should be specified with coma and I have an array?
Depending on your PHP version there are different approaches. I personally prefer argument unpacking, also referred to as splat-operator:
$this->em->getRepository($info['repoName'])->$info['funcName']->(...$info['params']);
This feature was introduced in PHP 5.6, see: https://secure.php.net/manual/en/migration56.new-features.php#migration56.new-features.splat
Another common approach is to use call_user_func_array where you provide a callable for what method should be called on which object and then an array of arguments. In your case it will probably look something like this:
call_user_func_array([$this->em->getRepository($info['repoName']), $info['funcName']], $info['params']);
Try call_user_func_array :
call_user_func_array([
$this->em->getRepository($info['repoName']), $info['funcName']
],
$arr['params']
);

What is use of "expect_file()" function in Codeception?

Hi i am new to codeception unit testing and i am using it with Yii2. I know the user of functions expect_not() and expect_that() and also know little about expect() function and uses it to check key in error array.
However I don't know the use of expect_file(). I searched little in internet but found not any good help. can anyone please give me little description about the use of this function.
expect_file() is used to verify with assertions specific to file system objects. It has two parameters (one is optional).
If you call this function with a single parameter, it will be used as the Subject Under Test file name. if it is called with two parameters, will be used as a description to display if the assertion fails but if you if you call it with 0 or more than two arguments it will throw a bad method call exceptions.
You can use it like this
expect_file('filename.txt')->exists();
expect_file('filename.txt')->notExists();
BTW expect_file() is an alternate function for verify_file().

PHPUnit how to test a specific method with specific parameter with specific answer for more than one call

I have tried testing multiple parameters for a specific method, and get different answers by the same mock for different parameters.
This is what I've done so far:
$mock = $this->getMockBuilder('MyClass')->disableOriginalConstructor()->getMock();
$mock->expects($this->any())
->method('myMethod')
->with($this->equalTo('param1'))
->will($this->returnValue('test1'));
$mock->expects($this->any())
->method('myMethod')
->with($this->equalTo('param2'))
->will($this->returnValue('test2'));
When I call $myClass->myMethod('param1') all is well and I get the 'test1'
However, here's the problem:
When I call $myClass->myMethod('param2') I get an error
Failed asserting that two strings are equal.
--- Expected
+++ Actual ## ##
-'param1'
+'param2'
A solution I have found is to just create a new mock for each call.
$mock1 = $this->getMockBuilder('MyClass')->disableOriginalConstructor()->getMock();
$mock1->expects($this->any())
->method('myMethod')
->with($this->equalTo('param1'))
->will($this->returnValue('test1'));
$mock2 = $this->getMockBuilder('MyClass')->disableOriginalConstructor()->getMock();
$mock2->expects($this->any())
->method('myMethod')
->with($this->equalTo('param2'))
->will($this->returnValue('test2'));
I do not know though why is this needed, perhaps I am using it wrong.
So the question remains:
How do I mock the same class, with a specific method, for different parameters and get different return values?
You can also simplify these statements a bit. If all you're needing to do is mock the function so that when you pass 'param1' you get back 'test1', then this should work:
$mock->method('myMethod')
->with('param1')
->willReturn('test1');
$mock->method('myMethod')
->with('param2')
->willReturn('test2');
If it's about dependency for a tested class, that is called twice inside tested method then it could be made like this
$mock = $this->getMockBuilder('MyClass')->disableOriginalConstructor()->getMock();
$mock->expects($this->at(0))
->method('myMethod')
->with($this->equalTo('param1'))
->will($this->returnValue('test1'));
$mock->expects($this->at(1))
->method('myMethod')
->with($this->equalTo('param2'))
->will($this->returnValue('test2'));
The first time it must be called with arg param1 then $mock->myMethod('param1') and will return test1, second - with arg param2 and will return test2.

PHPUnit Mock does not filter or ignores calls

I got a factory so this mock method is called "getModelMock".
It rewrites the setData method when the first argument is "template_text" to another function "fetchArgs" that stores the given arguments.
$coreEmailTemplateMock = $this->getModelMock(
'core/email_template',
array('setData')
);
$coreEmailTemplateMock
->expects($this->any())
->method('setData')
->with($this->equalTo('template_text'))
->will($this->returnCallback(array($this, 'fetchArgs')));
$this->replaceByMock('model', 'core/email_template', $coreEmailTemplateMock);
The method "setData" will be called at some time like setData('template_text', 'foo'). I know that because without the "with" clause I can see every argument that has been put in that function.
So to my understanding the first argument IS "template_text" at some time and I should see the "foo" too.
Without the with() I see every argument that has ever been parsed (my fetchArgs does that).
With the with() I see nothing.
What went wrong here? Something wrong with the with() I used?
thanks in advance!
cheers

Using function prototypes dynamically in PHP

I'm writing a construct in PHP where a parser determins which function to call dynamically, kind of like this:
// The definition of what to call
$function_call_spec = array( "prototype" => "myFunction",
"parameters" => array( "first_par" => "Hello",
"second_par" => "World"));
// Dispatch
$funcPrototype = $function_call_spec["prototype"];
$funcPrototype(); // Here we call function 'myFunction'.
This is all fine and dandy. But now comes the next step, passing the parameters, which I don't really know if it's possible the way I want to do it. It never stops amazing me however what script languages can do these days, so here goes:
One could pass the parameters to the function like this:
// Here we call function 'myFunction' with the array of parameters.
$funcPrototype( $function_call_spec["parameters"] );
However, I want to declare 'myFunction' properly with clear arguments etc:
function myFunction( $first_par, $second_par )
{
}
The question then follows - Is there any way to pass parameters to a function dynamically simply by looping through the parameter array?
To clarify, I don't want to do it like this:
$funcPrototype( $function_call_spec["parameters"]["first_par"],
$function_call_spec["parameters"]["second_par"] );
Because this requires my code to statically know details about myFunction, which goes against the whole idea.
Instead I would want to do it in some way like this maybe:
// Special magic PHP function which can be used for invoking functions dynamically
InvokeFunction( $funcPrototype, $function_call_spec["parameters"] );
Which then results in myFunction being called and all parameters in the array gets passed to each individual parameter variable in the prototype.
Any comments are welcome.
Regards.
/R
PS: None of the code in this post has been tested for typos etc.
You should use call_user_func_array which can call any function or method and takes parameteres from an array.
Alternatively you can use ReflectionFunction::invokeArgs, but there's no benefit over call_user_func_array unless you already use this class for someting else (like checking whether function you call accepts appropriate number and types of arguments).
call_user_func_array($funcPrototype, $function_call_spec["parameters"]);
You might want to create a wrapper that names the function to your preference, such as:
function InvokeFunction($function, $args = array()) {
return call_user_func_array($function, (array)$args);
}
With this function you can call it in 3 different ways:
$return = InvokeFunction('doStuff');
$return = InvokeFunction('doStuff', $single_arg);
$return = InvokeFunction('doStuff', $multiple_args);
call_user_func_array() is the best choice if you don't need to enforce the contract, otherwise use ReflectionFunction.
http://us2.php.net/create_function
When you use create_function(), your arguments are not evaluated until runtime. Pretty sweet.

Categories