How to mock private method – solutions
First, it is important to identify source of the problem. When you say
I need to mock this private method
All I can hear is
This class is poorly designed. I must use some workarounds or even ugly hacks in order to test it. I know I shouldn’t be going down that road, but it seems I have no choice.
Properly designed SOLID code will never put you in a position which will require you to do private mocking. Source of the problem? Bad design.
To better understand my point let’s take a quick look at a class building order XML. Suppose you want to test CreateDocument method:
CreateDocument is trivial to test if we can mock this discount calculation thing. With all the file-loading, date-parsing and what-not, it simply gets in our way. This is a lot of stuff we do not really care about, we should mock it. But we cannot. What to do then?
Single responsibility principle, first of the SOLID principles states that:
Every class should have a single responsibility (a reason to change - JK), and that responsibility should be entirely encapsulated by the class
In other words, class should do one thing and one thing only. OrderDocumentFactory, what does it do? It builds XML. If our requirements evolved so that we need to return slightly different XML, that is our one reason to change the class in question. Building XML is our single responsibility.
Unfortunately, OrderDocumentFactory also has second, hidden responsibility – it calculates discount. Had we decided bonus discount is no longer supported, we’ll have to modify OrderDocumentFactory class. This is strange because why would document factory had anything to do with discount calculation? If we wanted to change discount calculation logic we should be rather looking to modify DiscountCalculator class. And there goes our problem – we have one class pretending to be two2.
We already know what to do. We need a second class that will handle discount calculation logic. All right then, is this code unit testable now?
Not so fast. This “solution” adds another responsibility to CreateDocument method – instance creation. We do not want that. What if DiscountCalculator required some constructor arguments? Where would CreateDocument take them from? Instance creation is almost always out of scope of any class responsibilities. We need dependency injection:
With code structured like this you don’t have to mock private method but instead you mock dependency, IDiscountCalculator, which is the usual course of action.
In most of the cases the need to mock private method originates from poor design. Design that is already causing problems (inability to write unit tests) and will cause more problems as the code grows. Solution is simple:
- Fix your design by sticking to SOLID principles
- Class should do one thing and one thing only; the fact that you need to mock private method of your class usually indicates there is a second class with different responsibility hiding there
- Any additional responsibilities should be extracted as separate components and provided to class in question via dependency injection
Unit tests are much like your good buddy. When dealing with your code they will be first to ask “Wait a second, this cannot be right?”. Mocking private method cannot be right. And it is not.