views:

90

answers:

4

Let's assume we are designing a Stack class test-first (TDD):

public class Stack<T> {
    private T[] elements = new T[16];
    private int size = 0;
    ...
}

This Stack makes use of a size 16 internal array to store its elements. It will work fine until you need to add a 17th element. As I might need a 17th element, I decided to add that functionality to my Stack, so I started thinking about what name I could give to the test that'd make have to add that functionality. That will be the subject of my first question.

I first chose something of the form of:

Should_Be_Able_To_Correctly_Increase_Its_Inner_Array_Size()

and then

Should_Handle_More_Items_Than_The_Default_Internal_Array_Size()

but after thinking for a bit I came to the conclusion that maybe something like the following would be more apropriate:

Should_Double_Its_Size_Every_Time_Its_Full()

My reasoning would have to do that in the first case, I am saying only what it does, but not when. In the second, I am saying when to add more items, but I am also stating how I'm thinking of achieving it (internally), which may not be correct. In my view (I'm not sure I am correct), my tests should the possible interactions of my SUT with the exterior, and not on how it is implemented internally. Am I right?

It looks to me that the 3rd options is the best, as it clearly states what it does(grow in size -- in fact, double its size), when it does(when it's full) and doesn't ties me to any specific implementation (I could maybe later want to change it to an internal ArrayList!).

Which leads me to my second question: Assuming I did all the Unit-Tests for my Stack class that uses internally an Array and it works fine and as expected, should my tests remain intact if I later want to refactor and change the Array to an ArrayList or any other kind of data-structure? Or should the tests in any way reflect that? I'd guess no, but I'm not sure.

+2  A: 

I am not sure how you can the test the internal list or array size through a unit test. You can't access it through the Stack interface. Unit testing is for testing external contracts. If you want to test implementation details then try something else.

The answer to your 2nd question is yes, the test should still pass, if it's a unit test.

Timo Westkämper
When doing this class TDD style, what kind of tests would I be doing, then?
devoured elysium
Test the external interface and make sure it works as expected.
Timo Westkämper
+3  A: 

In my view (I'm not sure I am correct), my tests should the possible interactions of my SUT with the exterior, and not on how it is implemented internally. Am I right?

You are correct. If you change the internal implementation of your class, the unit tests should remain the same. If you expose anything new externally, you should create new unit tests to account for these changes.

Remember that when you're designing your class you don't want to expose anything that indicates how it was implemented. The class' public members indicate how to interact with it, now how it works behind the scenes.

Bernard
+1  A: 

I'm not sure if you should test the internal list or array size through a unit test, since you cannot access it through the Stack interface. There are many ways to implement a stack, some good, and some bad, but as Bernard stated, those are internal implementations. Unit tests are designed to test outward facing functionality.

Brian Stinar
+2  A: 

Ask yourself to what you are willing to commit for this class.

Do you or the class' consumers really care whether the capacity doubles, increments by one, or increments by one thousand? If so, you should modify the interface so that they can specify the strategy used to increase capacity:

public Stack<T>(CapacityGrowthStyle capacityGrowthStyle) { ... }

If not, just write tests to document the capacity and leave it at that (where X below is your underlying data structure's limit):

[Test]
public void Can_Handle_X_Items() { ... }

[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void Cannot_Handle_More_Than_X_Items() { ... }

I'd answer your second question similarly: your tests should only reflect the underlying data structure if your class' users will care about it.

Jeff Sternal
I get your points. Now, if I'm following TDD guide-lines, what should I do to add this "bit of functionality"? Now that I see it, that is my main question!
devoured elysium
@devoured - that's an interesting (and difficult) question. If the capacity growth algorithm really mattered to my users (presumably for performance reasons), I'd be tempted to add a `Capacity` property to facilitate testing, even though users probably wouldn't normally need that property themselves. But I'd feel some regret about cluttering the API. :(
Jeff Sternal
but let's say that my users won't want to know about it. using test-first methodologies, i should only add some specific functionality if i have made a test first that tests it. but in this case i'd be adding a new test that wouldn't matter to the outside world. how to "solve" this issue?
devoured elysium
I think it'll be easier to just create another topic on SO about it, as it'll not only get more attention but is (imo) a good question on its own.
devoured elysium
It's posted here: http://stackoverflow.com/questions/3604142/how-to-conciliate-tdd-with-sut-interfaces-contracts
devoured elysium
In that case, I'd say resizing isn't part of the interface or promised functionality, and wouldn't require a unit test.
Jeff Sternal