PHPUnit: Testing static calls
When you’re writing code, sometimes it’s necessary to use static calls. Maybe the only way to interface with an external library is through static calls. I must admit, it’s just easy to work with a static call. Until you have to assert in a unit test that you’re actually calling the static method. Then it gets more difficult.
Stubbing and mocking internal static methods
As Sebastian Bergmann explains in one of his blogposts, since PHPUnit 3.5 it’s possible to stub & mock static methods. In the same class. Here’s how it goes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
The unit test goes like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
So what happens here? The key is that we have 2 static methods, and in the first static method we do a static call to the second static method. All in the same class.
We tell the getMockClass
method to give us a mock classname for our Foo class, and we want to stub method baz
. Once we have this classname, we can use staticExpects
for our expectations.
Then we do our regular static call to the bar
method. But instead of doing it on our Foo
class, we do it on the mock classname. As such, the mock can use the stub we created when internally static::baz
is called.
Stubbing and mocking external static methods
So far that wasn’t too difficult. But how many times have you written code like this? Personally, I don’t ever write code like that. However, sometimes I have to work with an external library and then static calls might be the only available interface.
1 2 3 4 5 6 7 8 9 10 |
|
As you can see, the interface to the Log class makes us use a static call to log a message. Assume that you want to know if you actually do the Log::message
call in a unit test, then we’re screwed since the static call is external.
There is a way around that. It’s a hassle and it’s not beautiful. First we have to refactor the external static call a bit:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Instead of calling Log::mesage
directly, we use forward_static_call_array. The name of our log class is now defined in a property, and we use that property in the forward_static_call_array
method. Can you see where this is going? We’re kind of injecting the Log classname dependency. Not pretty, but it works!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Same as before, we use getMockClass
to get a mock classname for the Log class. Then we set this classname in our $loggingClass
property. Since it’s a protected property, I can only do that through Reflection. I just made the property protected to make it more difficult ;–).
So here we go, we were able to test an external static method call. It’s up to you to decide if you want to refactor your code like this. And it’s also up to you to decide if you want to actually assert these kinds of calls. I agree, we don’t really want to verify a call to a logger. But that was just an example.