tags:

views:

74

answers:

2

Is it possible to create a mock for the function?

UPD1:

$class->callback('callback_function');

I've tried to test whether callback_function was invoked once or not.

+5  A: 

It's called a function-mock, otherwise known as a fock :)

Just kidding! You might try PHPFunctionMock for this sort of thing.

Alex M.
hehe ;-) but: 1. unfortunately i'm limited with current testing workaround (phpunit) 2. "It uses the PECL runkit extension" <--- it was taken from PHPMockFunction start page.
zerkms
runkit is likely to be the only way you'd pull this off. PHP won't let you redefine a function that has already been defined.
Charles
yep, seems like i wanted something strange :-S
zerkms
+2  A: 

Native functions cannot be mocked. You would need something like runkit to do so.

What you could do though, is utilize a Strategy Pattern and wrap the native function calls into separate Command Objects or Closures/Lambdas and use those instead. Those can be passed around and exchanged freely.

Example 1 - Using a Lambda Function:

$callback = function() { 
    // a native function in here
}
$class->callback($callback);

Example 2 - Using a Command Object:

interface ICommand
{ 
    public function execute();
}
class Callback implements ICommand
{
    public function execute()
    { 
        // a native function in here
    }
}
$class->callback(array('Callback', 'execute'));

You can then mock those callbacks easily. I am not sure how PHPUnit implements the 'I have been called' thing. Either look into the sourcecode or add a subject/observer pattern.

Gordon
I've tried to cover callback invocation with unit test case, so strategy pattern is not suitable here. But thanks for opinion, +1
zerkms
@zerkms actually I don't see why it shouldnt be suitable. Just exchange the callback with a lambda or a command object that mocks the original callback. Maybe you can update the question to show what you are trying to test.
Gordon
I added an update
zerkms
Yep, but case 1 is not mock - i cannot specify with some declaration number of its callings. In second case - we will call method, **BUT** inside `callback()` implementation method and function are being invoked in slightly different manner, so we should test both.
zerkms
$mock->expects($this->once())->method('unit_test_callback') -- this is how phpunit accepts "number-of-calls" behaviour
zerkms
@zerkms A Mock substititutes for a dependency. PHPUnit does provide a convenient method for creating Mocks with certain functionality, but you can very much use your own, including that from Ex1 (yes, you cannot use that with `->expect()` then, but it's still a Mock). I do not know what you mean by *we should test both* for Ex2 as I do not know your implementation. The whole point of the examples is to illustrate how to wrap native functions in object callbacks or lambdas so they become mockable.
Gordon
Hmmmmm.... The issue is "how to pass function which will be mock", by saying this i mean - function, that acts with pre-set behaviour: expects specific parameters, returns specific value and being called only once. i can construct such mock-object, but for function it seems like there is no elegant solution.
zerkms
@zerkms Well, that's the most elegant solution I can offer. Pass in a [callback](http://uk2.php.net/manual/en/language.pseudo-types.php#language.types.callback) and it's arguments. Call it with [`call_user_func_array`](http://uk2.php.net/manual/en/function.call-user-func-array.php). Yes, you have to replace ALL function calls you want to mock - or use runkit.
Gordon
I do know what `callback` and `call_user_func_array` are. I do know how to invoke function. The issue is **how to test that function was invoked just once**. It is PHPUnit-specific question, not the question about some general php programming practices.
zerkms
@zerkms I understood that it is about PHPUnit specifically, but like Alex and I already told you: *functions* cannot be mocked with PHPUnit. I suggested to use your own Mock in that case. If that's not what you want, ok. Also, if this is specifically about `$mock->expects($this->once())->method('unit_test_callback')`, then you should change your question to reflect this and add what you expect 'unit_test_callback' to be if it is not a method in the mock. If it is a method in the mock, you can use the Strategy Pattern. And that's where it goes in circles.
Gordon
i'm confused.. but anyway - it is not possible to make "clean" mock for function without any additional code, due to php ideology. that's what i had to know ;-) thanks for the replies, and sorry if i was rude :-(
zerkms
@zerkms yeah, me too :) cheers
Gordon