114

I'm just now learning TDD. It's my understanding that private methods are untestable and shouldn't be worried about because the public API will provide enough information for verifying an object's integrity.

I've understood OOP for a while. It's my understanding that private methods make objects more encapsulated, thus more resistant to change and errors. Thus, they should be used by default and only those methods that matter to clients should be made public.

Well, it's possible for me to make an object that has only private methods and interacts with other objects by listening to their events. This would be very encapsulated, but completely untestable.

Also, it's considered bad practice to add methods for the sake of testing.

Does this mean TDD is at odds with encapsulation? What is the appropriate balance? I'm inclined to make most or all of my methods public now...

6
  • 9
    Bad practice and reality in the software industry are different animals. An ideal situation is more often than not a distorted reality in the business world. Do what makes sense and stick to it throughout the application. I'd much rather have a bad practice in place than the flavor of the month spread across the application. Commented Feb 14, 2012 at 16:07
  • 10
    "private methods are untestable"? Which language? In some languages it's inconvenient. In other languages it's perfectly simple. Also, are you saying that the design principle of encapsulation must always be implemented with lots of private methods? That seems a bit extreme. Some languages don't have private methods, yet, still seem to have nicely encapsulated designs.
    – S.Lott
    Commented Feb 14, 2012 at 16:10
  • "It's my understanding that private methods make objects more encapsulated, thus more resistant to change and errors. Thus, they should be used by default and only those methods that matter to clients should be made public." This seems to me like counter point of view of what TDD is trying to achieve. TDD is development methodology that leads you to create simple, workable and open-to-changes design. Looking "from private" and "only publicize..." is turned around completely. Forget there is such a thing as a private method to embrace TDD. Later, do them as needed; as a part of refactoring.
    – herby
    Commented Feb 14, 2012 at 16:31
  • Possible duplicate of Testing private methods as protected
    – gnat
    Commented Apr 3, 2018 at 12:14
  • 1
    So @gnat you think this should be closed as a duplicate of the question which arose out of my answer to this question? *8')
    – Mark Booth
    Commented Apr 4, 2018 at 14:00

14 Answers 14

66

Prefer testing to the interface over testing on the implementation.

It's my understanding that private methods are untestable

This depends on your development environment, see below.

[private methods] shouldn't be worried about because the public API will provide enough information for verifying an object's integrity.

That's right, TDD focuses on testing the interface.

Private methods are an implementation detail that could change during any re-factor cycle. It should be possible to re-factor without changing the interface or the black-box behaviour. In fact, that is part of the benefit of TDD, the ease with which you can generate the confidence that changes internal to a class will not affect users of that class.

Well, it's possible for me to make an object that has only private methods and interacts with other objects by listening to their events. This would be very encapsulated, but completely untestable.

Even if the class has no public methods, it's event handlers are it's public interface, and it its against that public interface that you can test.

Since the events are the interface then it is the events that you will need to generate to test that object.

Look into using mock objects as the glue for your test system. It should be possible to create a simple mock object that generates an event and picks up the resultant change of state (possible by another receiver mock object).

Also, it's considered bad practice to add methods for the sake of testing.

Absolutely, you should be very wary of exposing internal state.

Does this mean TDD is at odds with encapsulation? What is the appropriate balance?

Absolutely not.

TDD shouldn't change the implementation of your classes other than to perhaps simplify them (by applying YAGNI from an earlier point).

Best practice with TDD is identical to best practice without TDD, you just find out why sooner, because you are using the interface as you are developing it.

I'm inclined to make most or all of my methods public now...

This would be rather throwing the baby out with the bath water.

You shouldn't need to make all methods public so that you can develop in a TDD way. See my notes below to see if your private methods really are untestable.

A more detailed look at testing private methods

If you absolutely must unit test some private behaviour of a class, depending on the language/environment, you may have three options:

  1. Put the tests in the class you want to test.
  2. Put the tests in another class/source file & expose the private methods you want to test as public methods.
  3. Use a testing environment that allows you to keep test and production code separate, yet still allow testing code access to private methods of the production code.

Obviously the 3rd option is by far the best.

1) Put the tests in the class you want to test (not ideal)

Storing test cases in the same class/source file as the production code under test is the simplest option. But without a lot of pre-processor directives or annotations you will end up with your test code bloating your production code unnecessarily, and depending on how you have structured your code, you may end up accidentally exposing internal implementation to users of that code.

2) Expose the private methods you want to test as public or protected methods (really not a good idea)

As suggested this is very poor practice, destroys encapsulation and will expose internal implementation to users of the code.

3) Use a better testing environment (best option, if it is available)

In the Eclipse world, 3. can be achieved by using fragments. In the C# world, we might use partial classes. Other languages/environments often have similar functionality, you just need to find it.

Blindly assuming 1. or 2. are the only options would be likely to result in production software bloated with test code or nasty class interfaces that wash their dirty linen in public. *8')

  • All in all - it is much better not to test against private implementation though.
5
  • 5
    I'm not sure I'd agree with any of the three options you suggest. My preference would be to only test the public interface as you said before, but ensure that by doing so, private methods are exercised. Part of the advantage of doing so is finding dead code, which is unlikely to happen if you force your test code to break the normal usage of the language.
    – Magus
    Commented Mar 26, 2014 at 18:23
  • 1
    Your methods should do one thing, and a test should not consider the implementation in any way. Private methods are implementation details. If testing only public methods means your tests are integration tests, you have a design problem.
    – Magus
    Commented Mar 31, 2014 at 22:35
  • What about making the method default/protected and creating a test in a test project with the same package? Commented Feb 27, 2015 at 16:26
  • @RichardCypher That's effectively the same as 2) since you are altering what your method specs would ideally be to accommodate a deficiency in your test environment, so definitely still poor practice.
    – Mark Booth
    Commented Mar 3, 2015 at 20:07
  • I’ve found that putting the private methods into interfaces/classes at the business level instead of the abstraction level allows for nice isolation and test ability. It doesn’t pollute the “public” interface as those interfaces are declared at a high level. Optionally you can lock them down more by using friend/internal and expose only to an explicit set of libraries such as your test harnesses. Commented Oct 27, 2020 at 12:17
83

Of course you can have private methods, and of course you can test them.

Either there is some way to get the private method to run, in which case you can test it that way, or there is no way to get the private to run, in which case: why the heck are you trying to test it, just delete the damn thing!

In your example:

Well, it's possible for me to make an object that has only private methods and interacts with other objects by listening to their events. This would be very encapsulated, but completely untestable.

Why would that be untestable? If the method is invoked in reaction to an event, just have the test feed the object an appropriate event.

It's not about having no private methods, it's about not breaking encapsulation. You can have private methods but you should test them through the public API. If the public API is based on events, then use events.

For the more common case of private helper methods, they can be tested through the public methods that call them. In particular, since you are only allowed to write code to get a failing test to pass and your tests are testing the public API, all new code you write is usually going to be public. Private methods only appear as a result of an Extract Method Refactoring, when they are pulled out of an already existing public method. But in that case, the original test which tests the public method still covers the private method as well, since the public method calls the private method.

So, usually private methods only appear when they are extracted from already tested public methods and are thus already tested as well.

5
  • 4
    Testing through public methods works well 99% of the time. The challenge is the 1% of the time when your single public method has several hundred or thousand lines of complex code behind it and all of the intermediate states are implementation specific. Once it gets complex enough trying to hit all the edge cases from the public method becomes painful at best. Alternately, testing the edge cases by either breaking encapsulation and exposing more methods as private, or by using a kludge to have the tests call private methods directly results in brittle test cases in addition to being ugly. Commented Feb 14, 2012 at 19:57
  • 29
    Large, complex private methods are a code smell. An implementation which is so complex that it cannot be usefully decomposed into component parts (with public interfaces) is a problem in testability that reveals potential design and architectural problems. The 1% of cases where the private code is huge will usually benefit from rework to decompose and expose.
    – S.Lott
    Commented Feb 14, 2012 at 20:06
  • 13
    @Dan Neely code like that is pretty untestable regardless--and part of writing unit tests is pointing this out. Eliminate states, break up classes, apply all the typical refactorings and then write unit tests. Also with TDD how did you even get to that point? This is one of the advantages of TDD, writing testable code becomes automatic.
    – Bill K
    Commented Feb 14, 2012 at 20:34
  • At least internal methods or public methods in internal classes should be tested directly quite often. Luckily .net supports the InternalsVisibleToAttribute, but without it, testing those method would be a PITA. Commented Feb 15, 2012 at 16:35
  • The lack of test ability comes from how much code much be stood up or mocked to test a deeply nested call. Trying to test the fourth permutation though the second private call can (and will be) a glorious pain to setup and maintain. Small flat classes are easier to test and maintain. Commented Oct 27, 2020 at 12:20
28

When you create a new class in your code, you do it to answer some requirements. The requirements say what the code must do, not how. This makes it easy to understand why most testing happens at public methods level.

Through tests, we verify that the code does what it is expected to do, throws appropriate exceptions when expected, etc. We don't really care how the code is implemented by the developer. While we don't care about implementation, i.e. how the code does what it does, it makes sense to avoid testing private methods.

As for testing classes which don't have any public methods and interact with the outside world only through events, you can test this too by sending, through tests, the events and listening to the response. For example if a class must save a log file each time it receives an event, the unit test will send the event and verify that the log file is written.

Last but not least, in some cases it is perfectly valid to test private methods. That's why for example in .NET, you can test not only public, but also private classes, even if the solution is not as straightforward as for public methods.

1
  • 5
    +1 One important feature of TDD is that it forces you to test that REQUIREMENTS are fulfilled, rather than testing that METHODS do what they think you do. So the question "Can I test a private method" is a bit contrary to the spirit of TDD - the question might instead be "Can I test a requirement whose implementation includes private methods". And the answer to this question is clearly yes. Commented Feb 14, 2012 at 21:04
6

It's my understanding that private methods are untestable

I disagree with that statement, or I would say you don't test private methods directly. A public method may call different private methods. Maybe the author wanted to have "small" methods and extracted some of the code into a cleverly named private method.

Regardless of how the public method is written, your test code should cover all paths. If you discover after your tests that one of the branch statement (if/switch) in one private method has never been covered in your tests, then you have a problem. Either you missed a case and the implementation is correct OR the implementation is wrong, and that branch should never have existed in fact.

That is why I use Cobertura and NCover a lot, to make sure that my public method test also covers private methods. Feel free to write good OO objects with private methods and don't let TDD/Testing go in your way in such matter.

5

Your example is still perfectly testable as long as you use Dependency Injection to provide the instances that your CUT interacts with. Then you can use a mock, generate the events of interest, and then observe whether or not the CUT takes the correct actions on its dependencies.

On the other hand, if you have language with good event support you may take a slightly different path. I don't like when objects subscribe to events themselves, instead have the factory that creates the object wire up events to the public methods of the object. It's easier to test, and makes it externally visible what kinds of events the CUT needs to be tested for.

1
  • That's a great idea-- "...have the factory that creates the object wire up events to the public methods of the object. It's easier to test, and makes it externally visible what kinds of events the CUT needs to be tested for."
    – pup
    Commented Feb 14, 2012 at 16:44
5

You should not need to abandon using private methods. It's perfectly reasonable to use them, but from a testing perspective they are harder to test directly without either breaking encapsulation or adding test-specific code to your classes. The trick is to minimize the stuff that you know is going to make your gut squirm because you feel like you've dirtied your code.

These are the things that I keep in mind to try and achieve a workable balance.

  1. Minimize the number of private methods and properties that you use. Most of the stuff you need your class to do tends to need to be exposed publicly anyway, so have a think about whether you really do need to make that clever method private.
  2. Minimize the amount of code in your private methods - you really should be doing this anyway - and test indirectly where you can via the behavior of other methods. You never expect to get 100% test coverage, and perhaps you will need to hand check a few values via the debugger. Using private methods to throw Exceptions can easily be tested indirectly. Private properties may need to be tested manually, or via another method.
  3. If indirect or manual checking doesn't sit well with you, add a protected event and access via an Interface to expose some of the private stuff. This effectively "bends" the rules of encapsulation, but avoids the need to actually ship code that executes your tests. The drawback is that this can result in a little additional internal code to make sure that the event will be fired when needed.
  4. If you feel that a public method isn't "secure" enough, see if there are ways you can implement some sort of validation process in your methods to limit how they are used. Chances are that while you are thinking this through either think of a better way to implement your methods, or you'll see another class beginning to take shape.
  5. If you have a lot of private methods doing "stuff" for your public methods, there may be a new class waiting to be extracted. You can test this directly as a separate class, but implement as a composite privately within the class that uses it.

Think laterally. Keep your classes small and your methods smaller, and use lots of composition. It sounds like more work, but in the end you'll end up with more individually testable items, your tests will be simpler, you'll have more options for using simple mocks in place of real, large and complex objects, hopefully well-factored and loosely coupled code, and more importantly you'll give your self more options. Keeping things small tends to save you time in the end, because you reduce the number of things you need to individually check on each class, and you tend to naturally reduce the code spaghetti that can sometimes happen when a class gets large and has lots of interdependent code behavior internally.

4

Well, it's possible for me to make an object that has only private methods and interacts with other objects by listening to their events. This would be very encapsulated, but completely untestable.

How does this object react to those events? Presumably, it must invoke methods on other objects. You can test it by checking whether those methods get called. Have it call a mock object and then you can easily assert that it does what you expect.

The issue is that we only want to test the object's interaction with other objects. We don't care what's going on inside an object. So no, you shouldn't have any more public methods then before.

4

I've struggled with this same issue as well. Really, the way to get around it is this: How do you expect the rest of your program to interface with that class? Test your class accordingly. This will force you to design your class based on how the rest of the program interfaces with it and will, in fact, encourage encapsulation and good design of your class.

3

Instead of private use default modifier. Then you can test those methods individually, not just coupled with public methods. This requires that your tests have the same package structure as your main code.

2
  • ... assuming this is Java. Commented Feb 15, 2012 at 8:18
  • or internal in .net. Commented Dec 2, 2013 at 18:19
2

A few private methods usually aren't an issue. You just test them through the public API as if the code were inlined into your public methods. An excess of private methods may be a sign of poor cohesion. Your class should have one cohesive responsibility, and often people make methods private to give the appearance of cohesiveness where none really exists.

For example, you might have an event handler that makes a lot of database calls in response to those events. Since it's obviously bad practice to instantiate an event handler to make database calls, the temptation is to make all the database-related calls private methods, when they should really be pulled out into a separate class.

2

Does this mean TDD is at odds with encapsulation? What is the appropriate balance? I'm inclined to make most or all of my methods public now.

TDD is not at odds with encapsulation. Take the simplest example of a getter method or property, depending on your language of choice. Let's say that I have a Customer object and I want it to have an Id field. The first test I'm going to write is one that says something like "customer_id_initializes_to_zero". I define the getter to throw a not implemented exception and watch the test fail. Then, the simplest thing I can do to make that test pass is have the getter return zero.

From there, I go onto other tests, presumably ones that involve the customer ID being an actual, functional field. At some point, I probably have to create a private field that the customer class uses to keep track of what should be returned by the getter. How exactly do I keep track of this? Is it a simple backing int? Do I keep track of a string and then convert it to int? Do I keep track of 20 ints and average them? The outside world doesn't care - and your TDD tests don't care. That is an encapsulated detail.

I think this is not always immediately obvious when starting TDD -- you're not testing what methods do internally - you're testing less granular concerns of the class. So, you're not looking to test that method DoSomethingToFoo() instantiates a Bar, calls a method on it, adds two to one of its properties, etc. You're testing that after you mutate the state of your object, some state accessor has changed (or not). That's the general pattern of your tests: "when I do X to my class under test, I can subsequently observe Y". How it gets to Y is none of the tests' concern, and this is what is encapsulated and this is why TDD is not at odds with encapsulation.

2

Avoid using? No.
Avoid starting with? Yes.

I notice you didn't ask about whether it's ok to have abstract classes with TDD; if you understand how abstract classes emerge during TDD, the same principle applies to private methods as well.

You can't directly test methods in abstract classes just like you can't directly test private methods, but that's why you don't start out with abstract classes and private methods; you start with concrete classes and public APIs, and then you refactor common functionality as you go.

2

I'm curious how in the 9 years this question has been out there, nobody has pointed out that if you are doing TDD (Test-driven development), you should not be writing any method (public or private) unless it is to pass a failing test.

As such, it's an irrelevant question: tests force you to write public methods, which should in turn be refactored. That refactoring may involve extracting logic to private methods--but that only works if the tests still pass. That is, the "extract a method" involves creating a call to the new private method from a method that is already causing one or more tests to pass. Moreover, refactoring involves NOT adding logic, and TDD forbids adding logic if it is not being added to pass a failing test. So, if you're actually practicing TDD, all methods--public, private, protected, or whatever--will automatically be covered by the tests.

0

Your class has requirements. Roughly speaking, your class should have public methods which correspond to the requirements, and no others.

And your class needs code to implement the requirements. Depending on the requirements, it might need a lot of code. And for various reasons your methods shouldn’t be too large.

If you insist on no private methods, then all the code must be put into the public methods which is obviously rubbish. So you add exactly as many private methods as you need to have a class with good code. Testability is not everything. Actually, testability is nothing if you violate good coding principles and end up with code that is always too buggy to pass your tests.

Methods are made private to prevent every old bit of code to call them. That doesn’t mean you don’t want to test them. For example on MacOS / iOS you just mark a method as @testable and it can be called like a public method when you are building unit tests or other tests, but not when you are building an application.

Not the answer you're looking for? Browse other questions tagged or ask your own question.