The article continues the series of questions and answers about the problems of unit testing in PHP applications. In this part we’ll consider PHPUnit and mocking, such as disadvantages of the classical mocking framework and feature of its native alternative Prophecy, highlight common fallacies of how the test doubles should be used to make the object oriented design better.
Table of contents
- Why can’t I mock private methods in PHPUnit, if I can mock protected ones?
- Making private methods protected for sake of mocking, is it very bad?
- Why mocking protected methods is a bad practice?
- Why then PHPUnit allows mocking protected methods?
- How to mock final methods?
- I heard PHPUnit mocking framework is not recommended for usage. Why?
- If not PHPUnit mocks, then what?
- What about mocking 3rd party applications like APIs?
The technique that is used by PHPUnit to create test doubles and redefine their behavior is inheritance. A test double is a child of its original. Private methods cannot be overloaded in children, whereas protected methods can – it’s fundamentals of OOP.
You should be allowed to mock neither private methods, nor protected ones. Both would be hacks, not tools.
Yes, it is very bad.
TDD SHOULD GUIDE YOUR DESIGN, NOT RESIST IT.
Previously we considered detailed why we should only unit test public methods and how typical delusions can be dispelled.
It’s not just bad, it’s extremely bad practice. Strictly speaking it should not be allowed to create test doubles for anything but interfaces. Neither classes, nor abstract classes. It follows at least from the Liskov substitution principle.
MOCKING IS NOT A TOOL TO HACK ENCAPSULATION. MOCKING IS FOR TESTING EXPOSED MESSAGING BETWEEN OBJECTS.
Probably because PHPUnit was created long time ago. The theory of Test Driven Development has evolved a lot since then. But PHPUnit is a framework, used in many thousands of projects, many of which are not that nice enough to rely on only the best practices of unit testing, but they are still in active life cycle and they also require new versions of PHPUnit. They can’t be just cut off of the support.
The answer may seem boring: you don’t have to. And here’s why.
As it was already said PHPUnit natively creates mocks as inherited classes with overloaded methods. It may confuse: you can mock protected methods, but can neither private, nor even public final ones. But let’s stop thinking of mocking framework’s behavior and consider the problem from the point of the OOP architecture.
When do we use the final keyword for methods? When we forbid their overriding and fix their behavior. Why? Because a non final method implicitly guarantees that overriding it with another implementation will remain its class work as expected.
WE MAKE A METHOD FINAL WHEN WE CAN’T GUARANTEE THAT THE CLASS IT BELONGS TO SUPPORTS THE OVERRIDING.
So if there are not guarantees, that mocking a method will not change the rest work of the class, why should it be allowed in tests? Such tests just can’t be entirely correct and you shouldn’t rely on them.
PHPUnit test doubles aren’t really designed to educate. I could highlight few major problems: it…
- allows to mock protected methods;
- allows to call non-existent methods on mocks;
- doesn’t react on not predicted calls on test doubles (so it doesn’t help to highlight the Liskov substitution principle violation);
- engages in fact to couple with the implementation.
PHPUnit mocking framework kind of tempts developers into hacking objects rather than designing code with the SOLID conception respected. I share concerns that if bad practices are not restricted by frameworks they will always be engaging developers not to avoid poor design. That’s why I insist, that using PHPUnit mocking subframework should be a deprecated practice, as of today.
After all, there are evidences that Sebastian Bergman (the creator of PHPUnit) himself agrees that his mocking framework is “a broken tool”, so he himself doesn’t recommend it.
As of today, I prefer Prophecy. The huge advantage of it as it’s now a default part of the PHPUnit distributive (since the version 4.7). So in fact you don’t really stop using precious PHPUnit, we have to be grateful so much to.
If you’re interested in BDD, try out also PHPSpec Framework (Prophecy originates from it).
In the first place you should not call 3rd party classes directly. Built an adaptor layer inbetween, so it will implement only required functions and will be easily available. On the top of it there should be an interface(-s) and classes you own should only collaborate with these interfaces. Interfaces can be easily mocked.
The most common misunderstandings of using the mocking approach in unit testing are usually about attempts to hack the design and create doubles for protected methods. We have considered, how the technique should be used instead and what should be improved in the object oriented design to conform the practices of writing the testable code. Another aspect is that if you want a mocking framework to be strict and educative, you should not use the original PHPUnit test double tool, better consider the Prophecy alternative, which is now also a native part of the PHPUnit distributive.
AUTHOR: ALEXANDER PALAMARCHUK