Unit testing private methods



I’m starting a new series about unit testing anti-patterns. This post is the first article in that series.

When it comes to unit testing, one of the most commonly asked questions is: how to test a private method?

Unit testing private methods

I think one of the biggest misconceptions in unit testing is this notion that, when you test a class, you should cover each and every method in it with unit tests. A logical extension of this thought is to try to cover private methods with tests too. Just make them public – not a big deal! – and be done with it.

Well, that’s a horrible approach to unit testing. By exposing methods that you would otherwise keep private, you reveal implementation details. And by coupling tests to those implementation details, you get a fragile unit test suite.

When your tests start to know too much about the internals of the system under test (SUT), that leads to false positives during refactoring. Which means they won’t act as a safety net anymore and instead will impede your refactoring efforts because of the necessity to refactor them along with the implementation details they are bound to. Basically, they will stop fulfilling their main objective: providing you with the confidence in code correctness.

When it comes to unit testing, you need to follow this one rule: test only the public API of the SUT, don’t expose its implementation details in order to enable unit testing. Your tests should use the SUT the same way its regular clients do, don’t give them any special privileges. Here you can read more about what an implementation detail is and how it is different from public API: link.

The answer to the question of how to test a private method is then: nohow. Just don’t do that. Let the SUT sort its behavior out the way it wants, only test the observable results of that behavior. The behavior the SUT’s clients can observe should be the one – and the only! – target for verification in tests.

But what if the private method is too complex and leaving it untested is too dangerous? What if there’s too much logic in some private method and unit testing it through the SUT’s public API is not feasible?  That’s an indicator that you miss an abstraction here. Instead of making this method public, extract its inner workings into a separate class and test that class.

Look at the following example:

Here, the order’s GenerateDescription() itself is quite simple: it just returns some generic description of the order. But it uses the private GetPrice() which is much more complex: it contains an important business logic which needs to be thoroughly tested. This complexity is a strong sign of a hidden abstraction.

Instead of making this method public, you can introduce a separate domain concept, PriceCalculator:

This new class now can be tested separately from Order. And you can also use the functional style of unit testing here because this class itself doesn’t maintain any internal state: it generates the output based on the provided input.

Unit testing internal classes

There’s another related problem that often arises along with the problem of unit testing private methods. And that is: how to test internal classes? The situation with them is not as straightforward as with private methods.

Let’s extend our example with the Order and PriceCalculator classes and say that we want to make PriceCalculator internal because it’s not used anywhere outside the domain model. Which is all located in the single assembly.

That’s a reasonable decision. It’s preferable to keep the domain model’s API surface as small as possible and not expand it without necessity.

We could use the InternalsVisibleTo attribute on the domain assembly and make the internal classes visible to the unit tests. But wouldn’t it entail the same problem we had with private methods? In other words, wouldn’t we couple the tests to internal implementation details and make them fragile? After all, there’s no client outside the domain model that uses PriceCalculator, so why would we provide the unit tests with the special privilege like that?

That’s a fair question. And the answer is: no, we wouldn’t be coupling the tests to implementation details.

To see why let’s take a look at the two requirements I brought up earlier:

  • Unit tests should use only the SUT’s public API.
  • Unit tests should mimic the behavior of the SUT’s clients.

While it’s true that PriceCalculator itself is internal, it still has a public API and clients that use that API. It’s just both the calculator and its clients are located in the same assembly.

We can depict this situation as follows:

Unit testing private methods: Unit testing an internal class

Unit testing an internal class

The domain model here is represented as a green circle. While there are no external clients using it, the domain model itself consists of multiple layers and members of outer layers utilize the inner layers in order to achieve their goals. They act as clients in relation to members of those inner layers.

So in order to unit test a member of the inner layer, we can use the same public API used by the clients from the domain model. In our case with Order and PriceCalculator, we can write tests against PriceCalculator.Calculate() because that’s the API the Order class invokes.

The fact that PriceCalculator and the unit tests reside in separate assemblies is just a technical inconvenience here. It shouldn’t stop us from unit testing this class. Despite being internal, it still has a public API which we can bind to in tests.

And I personally don’t even make those classes internal anymore, although they don’t have “normal” clients outside the same assembly. It simplifies unit testing at the expense of widening the domain model’s API surface but I find this trade-off worth making.

Summary

  • Don’t aim at unit testing each method in the SUT. Unit test only the publicly available API.
  • When writing unit tests, mimic the behavior of the SUT’s clients.
  • Don’t test private methods. Either unit test them indirectly, using the public API, or extract them into separate classes and test those classes instead.
  • Don’t hesitate to unit test internal classes as long as you follow the guidelines above.

If you enjoy this article, check out my Pragmatic Unit Testing training course, you will love it too.

Other articles in the series

Other series on the topic of unit testing

Share




  • Lucas Espindola

    Thanks, always valuable contributions Vladimir

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Thank you!

  • Иван Якимов

    Thank you for great articles, Vladimir. I’d like to ask you about your opinion on private classes. I would like to use them more, because their influence is limited to the parent class. It make me more comfortable when refactoring. But inability to test them directly prevents me from broad usage of private classes. What can you say about it?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Hi Ivan,

      I would say that the situation with private classes is different from that with internal ones. I wouldn’t recommend testing private classes directly. Instead, try to test them through the public class they belong to. This way you won’t bind to a particular implementation of them.

      It all depends on the context, though. In some cases, it might make sense to extract them into internal/public ones but generally, I lean towards not doing that and not testing private classes at all.

      • Olumide

        Wouldn’t testing private functions through public functions mean that you’re testing two things instead of one? As I recall good tests ought to be independent.

        • http://enterprisecraftsmanship.com/ Vladimir Khorikov

          Tests ought to test independent pieces of *behavior*, not code. The way the code is structured in essentially an implementation detail. A single public method, a public method and a private one, or a public method and a private class – it’s all good as long as they constitute a cohesive, single piece of behavior.

  • toby hei

    Always enjoy your unit testing posts Vladimir!

    One question on the price calculator class. It seems like it’s fundamentally a function rather than a class, do you have any opinions on using static classes in cases like this?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      My view on static classes has shifted over time. I used to be a big opponent of static classes. After getting familiar with functional programming, I changed my opinion and actually don’t mind them anymore. I wouldn’t say that I prefer a particular style over the other but in this particular case, a static class with a function in it would be a good fit for the price calculator, indeed.

  • Mathias Stenbom

    I often find that those internal classes tend to be refactored more often, and breaking the compatibility with any tests testing those internals. Whats your suggestion on how to handle that scenario?

    Remove the tests, and rely on the tests for the public API of the SUT to cover it?
    Refactor the tests while you refactor the internal?
    Create a test facade for those internals?

    Iv come across different ideas on how to solve this. Also, what about of the internal end up only being used by the tests, and not used by the SUT any more? But thats a different question I guess…

    Thanks for all your great blogs!

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      If your tests break every time you refactor something in the SUT, it could be a bad sign. It depends on what exactly breaks, though. If it’s just the signature and small compilation errors (e.g. after adding a new parameter to the constructor), that’s mostly fine as it can be handled pretty easily. But if the tests start to show a failure when in reality everything really works as before, that means those tests are tightly coupled to the implementation details (more on this here: http://enterprisecraftsmanship.com/2016/06/01/unit-tests-value-proposition/ ).

      If it’s the latter, raise the level of abstraction on which your tests operate. In your case, the solution could be to stop testing the internal classes and focus on the SUT’s API instead.

      If an internal class is used by tests only and not by production code, delete it along with the tests covering it. No need to keep dead code.

      • Mathias Stenbom

        Exactly.

        What is it that doesnt make an internal class an implementation detail? If it so happens that the testing testing an internal class fails one day when refactoring, would that pretty much define it as an implementation detail?

        Maybe what im saying is that I often find testing internal classes one day shows they are actually implementation details.

        • http://enterprisecraftsmanship.com/ Vladimir Khorikov

          It all depends on how you draw the line between the internal class and the production code that uses it. If the production code is tightly coupled to that class, the class becomes an internal implementation detail. There’s not much sense in testing the internal class separately then. If it’s independent, then it could be tested separately.

  • jamesej67

    It’s great to see such a sane approach proposed, and with such clear arguments to support it. For me this is all about encapsulation. The purpose of encapsulation is to allow you to change implementation details without breaking clients of your code. Therefore you want to draw encapsulation boundaries at a point where you can reasonably maintain some stability in the API. Clients of your code includes tests, because like any other client you don’t want to have to change your tests when you refactor. So this means the most efficient tests in terms of coding effort are those you write against the most stable encapsulation boundaries, which are generally what are called integration tests.

  • Igor Kovac

    I loved your plural sight session Vladimir. One question here: If PriceCalculator Calculate method for some reason has to go to database wouldn’t var calculator = new PriceCalculator() inside of GenerateDescription be a bad thing? How would you approach that from FP perspective?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      It would. I’d recommend against using impure services inside the domain classes. The general approach in this case would be to fetch all data required for the calculation beforehand (in an app service) and pass it to the domain model. Probably by adding a parameter to Order’s GenerateDescription().

      • Igor Kovac

        Thanks and keep good work Vladimir 🙂

  • Martin Fay

    I’ve recently gone through similar thinking, but for the “internal” classes I added a .Internal sub-namespace and left the classes public. This avoids messing around with “internals visible to”, but keeps the “internal” classes (obviously I hope) out of the public API seen in Intellisense.
    On the other comment about statics… it’s static state which is evil! And transitive coupling.

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Great point about the static state! And interesting suggestion about the internal namespace. I usually just leave them public with the classes that comprise the public API. Will try doing this instead.