views:

48

answers:

3

Assuming we are implementing using TDD a Stack class, we would need, for each bit of functionality of our Stack class, to add a new test that exercises it:

[TestMethod] public void Should_Be_Empty_After_Instantiation()
[TestMethod] public void Should_Not_Be_Empty_After_Pushing_One_Item()
...

Now, on the other hand, when doing Unit-Tests, one should focus on what external behaviour our class is supposed to provide, so the set of Unit-Tests check that all the expected contracts for my Stack interface are fulfilled.

My question is in how to conciliate those two aspects.

For example, assuming my Stack uses internally an array with an initial size of 8, I'll want it to grow if my user wants to insert a 9th item. For adding that resizing functionality, I'll want to have at least one test that drives my class code in that direction (am I right?).

On the other hand, that would be adding a Unit-Test (or isn't this really a Unit-Test?) that exercises not the actual contract of the class(I am assuming the user doesn't care for the internal implementation of the Stack) but its implementation.

So we have a twist here, that I don't know how to solve. Am I making any confusion of concepts here?

Thanks

Edit

After much googling I came to the following link that seems to deal with this issue: http://stephenwalther.com/blog/archive/2009/04/11/tdd-tests-are-not-unit-tests.aspx

+1  A: 

I would say they are the same thing when refering to TDD and external behaviour of a particular class. When writing TDD style code you are focusing on what the behaviour is when using the public api of the class. So your first two test cases are correct since they test something public on the class (size of stack).

As for internal implementation and whether you use an array, the test is not concerned with how it is done, just that the functionality works against the given test. You may choose to implement it differently at a later stage and your tests will verify that the behaviour remains unchanged (tests don't fail after the refactor)

aqwert
So if I got you right, when adding test methods for push/pop functionality to my Stack, and I'm using an internal Array, I must at the same time add reallocation code to the class(although no test will explicitly worry about reallocation, as it is an implementation detail). Is that correct?
devoured elysium
If adding the 9th item causes it to fail when it shouldn't, then you can consider that as a bug. You should write a test that exposes that bug and when it is fixed should pass. This will also work for any future changes to the allocation method.
aqwert
That was not my main point. My concern is in how to apply TDD here, as TDD tells that you should implement no code unless you have written a test first. But in this case, introducing a test first about that 9th element would go against only having unit-tests that test the interface stack behaviour contract.
devoured elysium
Still applies here. When you write the test for adding an item and your implement it with a limit of 8 then you can have the next test highighting that it will fail in having more than 8 which is still writing the test first. That test still tests the public API and behaviour. This still follows TDD and the Red-Green-Refactor approach. In fact you should write just enough code to pass the test, which in reality might mean the first test does drive all the functionality of the method. You will end up having more tests for the single method.
aqwert
The reason I say this could be written as a bug test rather than a normal test is becuase it is highlighting a gap in the internal implementation of the API.
aqwert
Sorry, I am not familiar with the term "bug test". Does it have some special meaning other than a test to try to catch a bug?
devoured elysium
So for this Stack class I should have something like a StackTests testing class (for all the tests concerning the general interface) and let's say..a StackImplementationTests class for those other kinky tests that tie us to this given implementation. Is that right?
devoured elysium
Not necessary to have a separate class although you can. You can attach the CategoryAttribute to highlight it is the case or simply comment. When looking at the test many months later and you see this edge case or test that fixes a bug you know why the test is there.
aqwert
+3  A: 

You could write a test that pushes a ninth item onto the stack. That would clearly fail if you don't have any resizing logic. However, hard-coding the 9 into the test seems like a bad idea, since you would be incorporating internal implementation details of the Stack into the tests.

Now, the writing of TDD tests often informs the author of possible gaps in his API. In this case, the test would like to be able to specify the initial pre-allocation size of the Stack. Then it could set it to 8 or 2 or whatever and push one more item than that. And, it is not unrealistic to think that other clients might want that too (it is similar to the reserve method of std::vector, for example). So, I would think about adding a constructor parameter to Stack to specify initial reserved size, default it to 8, and add a Should_Not_Error_When_Pushing_More_Items_Than_Initial_Size test.

SCFrench
The problem I see with that, is that if later I want to use an internal implementation for a Stack that uses, let's say for instance, a Tree, it might make no sense to have a Capacity or PreAllocationSize. In my view, if I am understanding this correctly, I should be able to make my tests work for both Tree / Array / ArrayList implementations (as the common interface is the same). Following this idea, having something as a capacity or allocation size would be more a smell than any other thing (a so called Leaky-Abstraction http://en.wikipedia.org/wiki/Leaky_abstraction).
devoured elysium
To recap, I don't particulary like the 2nd constructor sugestion but I can't see any other viable alternative when TDDing.
devoured elysium
A: 

You've got a couple of aspects of behaviour here:

  • a stack which allows items to be pushed and popped
  • a storage element which grows its internal array when another item is added.

If you're having problems testing them both, you could always put the behaviour which grows the internal array into another class, keeping it separate from the behaviour of the stack. It's the Single Responsibility Principle in overdrive! You could then just use a very large array in testing the behaviour of your stack, and check that the stack delegates its responsibilities correctly where growing is concerned using mocks (you can roll your own if you don't have a mocking framework).

I've found this a useful technique for timing, threading, or anything else which encapsulates internal behaviour that can't necessarily be seen from the outside. Of course, you sacrifice performance to some degree. If that's important then just worry about the behaviour of the stack from the point of view of its calling class, and use system performance and profiling tests to pick up the rest.

Lunivore