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.