tags:

views:

220

answers:

4

A very specific question from a novice to TDD:

I seperate my tests and my app into different packages. Thus, most of my app methods have to be public for tests to access them. As I progress, it becomes obvious that some methods could become private, but if I make that change, the tests that access them won't work. Am I missing a step, or doing something wrong, or is this just one downfall of TDD?

+9  A: 

You don't specify what language you are using, but certainly in most of them you can put the tests in a way that have more privileged access to the class. In Java, for example, the test can be in the same package, with the actual class file being in a different directory so it is separate from production code.

However, when you are doing real TDD, the tests are driving the class design, so if you have a method that exists just to test some subset of functionality, you are probably (not always) doing something wrong, and you should look at techniques like dependency injection and mocking to better guide your design.

Yishai
I ran into a similar problem in C# awhile ago - in that language/framework, you want the `InternalsVisibleTo` directive.
Tim
+1  A: 

At least in Java, it's good practice to have two source trees, one for the code and one for the tests. So you can put your code and your tests in the same package, while they're still in different directories:

src/org/my/xy/X.java
test/org/my/xy/TestX.java

Then you can make your methods package private.

Chris Lercher
+13  A: 

This is not a downfall of TDD, but rather an approach to testing that believes you need to test every property and every method. In fact you should not care about private methods when testing because they should only exist to facilitate some public portion of the API.

Never change something from private to public for testing purposes!

You should be trying to verify only publicly visible behavior. The rest are implementation details and you specifically want to avoid testing those. TDD is meant to give you a set of tests that will allow you to easily change the implemenation details without breaking the tests (changing behavior).

Lets say I have a type: MyClass and I want to test the DoStuff method. All I care about is that the DoStuff method does something meaningful and returns the expected results. It may call a hundred private methods to get to that point, but I don't care as the consumer of that method.

Josh
But you **do** want a way to test those hundreds of internal methods, no?
Ian Boyd
@Ian - If you are doing TDD, then they should already be tested. Their very existence means they were put there in order to make a test pass. However, those methods are an implementation detail that should not be known to the consumer of the class. If I figure out later that I can refactor those 100 methods into 50 methods, I shouldn't have to rewrite my tests. So... yes you test them, but not directly because you only care about the public API. If there is some private method that can't be exercised through the public API, then it is useless and should be removed.
Josh
I am following David Astels 'Practical Guide' - he indicates that you do test those things and then refactor when there is too much intamacy - that would have me change a lot of methods from public to private (not private to public) but then refactor my tests sets to remove the ones that are no longer testing public behavior - does this sound reasonable?
RoryG
Theeoretically you are right, but in practice it might be very hard to come up with a way of testing all possible paths inside DoStuff.
erikkallen
Absolutely. You should refactor your tests just as much as you refactor you underlying code. However, I think you always start with the public API in mind. In my experience this usually doesn't change a lot. In the end what I am saying is this: "If you are creating a private method during your refactoring phase, and it only serves to facilitate a public method, then it should stay private." Don't make it public just because you feel like you need to have every method tested. Think Behavior and not Test... Something I know David Astel is a big proponent of.
Josh
@erikkallen - At that point it would be time to refactor your class and or API in order to make it simpler ;)
Josh
It would be helpful to me if you expanded your answer to give an example how you would solve the OP's problem.
Ian Boyd
+4  A: 

This is where the old saying, "TDD is about Design," frequently comes up. A class with too many public methods probably has too many responsibilities - and the fact that you are test-driving it only exposes that; it doesn't cause the problem.

When you find yourself in this situation, the best solution is frequently to find some subset of the public methods that can be extracted into a new class ("sprout class"), then give your original class an instance variable of the sprouted class. The public methods deserve to be public in the new class, but they are now - with respect to the API of the original class - private. And you now have better adherence to SRP, looser coupling, lower cohesion - better design.

All because TDD exposed features of your class that would otherwise have slid in under the radar. TDD is about Design.

Carl Manaster