views:

444

answers:

10

There is one aspect of TDD which I never fully understood.

Suppose somebody asked you to implement a simple Stack object. If you've done your design properly, you will come to a very minimal and clean API. Suppose: push(), pop() and isEmpty(). Anything more than that is over-killing the demand, and allowing the user too much room to mess with your code.

So now let's suppose you want to unit test your code. How do you go about doing this if all your public methods are just the three shown above? Those methods will take your testing only so far.

So either you add private methods, which do not help you at all since they are not visible to your unit test case. Or you make those methods public and there goes your minimalistic API which you worked on so hard. Now the user is going to mess with your Stack and the bugs will be sure to come.

How do you handle this dilemma of opening public methods for testing vs. a clean and simple API?

Edit: just to point in the right direction, It would be nice to get technical pointers (such as "use this hack to expose private methods", etc...) but I am much more intrested in more generic answers as to what of the two concepts is more important, and how you approach this subject.

+1  A: 

Sometimes I make methods which would otherwise be private into package level (Java) or internal (.NET) methods, usually with a comment or an annotation/attribute to explain why.

Most of the time, however, I avoid doing this. What would the public API not let you test in your stack case? If the user can see the bug and they're only using the public API, what's stopping you from making the same calls?

The times I expose otherwise-private methods is when it makes it easier to test one part of a complicated set of steps in isolation - if a single method call is very "chunky" it can be very helpful to test each step in isolation, even though the steps shouldn't be visible to a normal user.

Jon Skeet
+10  A: 
  1. test the features; this usually means testing the public interface - for should not all features be accessible via the public interface? if they aren't, then they aren't features! There may be exceptions to this, but i can't think of any.

  2. test the public interface; any methods that are not called directly or indirectly from the public interface are not necessary. Not only do they not need to be tested, they do not need to exist at all.

Steven A. Lowe
I recommend this clarification: if you start to feel the need to expose something private for testing, DON'T, but view that as a sign that your implementation is too complex and that you need to rethink the granularity of your implementation and its testing.
Rob Williams
+5  A: 

You should take a look at that question : do you test private method?.

To not break the encapsulation, I find that the private method is huge or complex or important enough to require its own tests, I just put it in another class and make it public there (Method Object). Then I can easily test the previously-private-but-now-public method that now lives on it's own class.

Daok
A: 

You can test private with Reflection but it will be a pain later. I think you need to put your test in the same assembly (or package) and try to use Internal. This way you have some protection and you can test your stuff that you want to test.

Rogez Sanchez
+2  A: 

right TDD is all about testing behavior which could be tested by public interface... if there are are any private methods then those methods should be indirectly tested by any public interface...

StackUnderflow
+2  A: 

With TDD, all of your code should be reachable from your public interface:

  • First you write the tests for your features.
  • Then you write the minimum amount of code for your tests to pass. This is the indication that your features are implemented.

If some code is not covered, this means that either this code is useless (remove it and run your tests again) or your tests are incomplete (some features have been implemented but not explicitely identified by a test).

mouviciel
+3  A: 

Using your Stack example, you really shouldn't need to expose any inner workings to unit test it. Reiterating what others have said, you should feel free to have as many private methods as needed, but only test through your public API.

[Test]
public void new_instance_should_be_empty()
{
  var stack = new Stack();

  Assert.That(stack.IsEmpty(), Is.True);
}

[Test]
public void single_push_makes_IsEmpty_false()
{
  var stack = new Stack();

  stack.Push("first");

  Assert.That(stack.IsEmpty(), Is.False);
}

Using a combination of pushing and popping you can simulate all behaviour of the Stack class, because that's the only way a user has to interact with it. You don't need any tricks because you should only be testing what others can use.

[Test]
public void three_pushes_and_three_pops_results_in_empty_stack()
{
  var stack = new Stack();

  stack.Push("one");
  stack.Push("two");
  stack.Push("three");
  stack.Pop();
  stack.Pop();
  stack.Pop();

  Assert.That(stack.IsEmpty(), Is.True);
}
James Gregory
A: 

When coding using TDD, I create the public interface API for the object. That would mean in your example that my interface which the class implements would only have push(), pop() and isEmpty().

However testing these methods by calling them aren't unit tests in themselves (more on this at the end of this post), since they most likely test the co-operation of multiple inner methods which together produce the desired result and this is what your question is about: Should those methods be private?

My answer is no, use protected (or the equivalent of it in the language of your choice) for those instead of private which means that if your project and test POMs are structured similarly, the test suite class can see inside the actual class since they're virtually in the same folder. These can be considered unit tests: You're testing functional blocks of the class itself, not their co-operation.

As for testing the individual interface/API methods it's of course important to do that too and I'm not disputing that, those however fall somewhere between the hazy line of unit testing and acceptance testing.

In my opinion the most important thing to remember here is that unit tests tell you if a method misbehaves, acceptance tests tell if the co-operation of multiple methods'/classes' misbehave and integration testing tells if multiple systems' co-operation misbehaves.

Esko
A: 

If your private method is not indirectly tested by the public API test methods and it needs to be tested then I would delegate your main class to another secondary class.

public Stack {
    public ... push(...) {...}
    public ... pop(...) {...}
    public ... isEmpty(...) {...}

    // secondary class
    private StackSupport stackSupport;
    public StackSupport getStackSupport() {...}
    public void setStackSupport(StackSupport stackSupport) {...}
}

public StackSupport {
    public ...yourOldPrivateMethodToTest(...) {...}
}

Then your private method is a public method in another class. And you can test that public method in the another class tests. :-)

Banengusk
A: 

Here's another question on the same topic:

How do you unit test private methods?

Don Kirkby