Using Multiple Fixtures
I remember reading an article when I was first learning about jUnit. I don't remember who the author was, but I do remember this: the author described the setUp method in TestCase as setting up a fixture, which was described as an object in a known state. If you want to test that object in more than one state, you use more than one fixture in the form of multiple TestCases. At the time I really didn't get it. This was all sort of new to me and everyone around me who did unit test their code (there were a couple) was writing one TestCase per class and sometimes one test per method as well.
Recently I was guiding some students through the exercise of test driving a Stack implementation. We started off with a single TestCase. Here are the names of the test methods that evolved.
StackTest {
testSizeShouldBeZeroAfterInit
testSizeAfterPushingOne
testSizeAfterPushingTwo
testPeekWithOneElement
testPeekWithTwoElements
testPopWithOneElement
testPushAndPopTwoElements
testPopOnEmptyStackShouldThrowEmptyStackError
testShouldAllowTenItems
testPushOnFullStackShouldThrowStackOverflowError
testPush10Pop1Push1More
}
When we got to this point, we all agreed that the implementation was done, but we decided that while looking at the code, not the tests. Looking over the TestCase it was difficult to see whether we had really specified everything we wanted to. Remember, tests are not just tests, they are user guides for future development. They are executable specifications.
So I introduced the idea of multiple test cases per class. There was some resistance, but we agreed to try it and see what would happen. So we started with an empty stack, pulled in all the methods related to an empty stack, renamed a couple of the methods and ended up with this:
EmptyStackTest {
testSizeShouldBeZero
testSizeShouldBeOneAfterPush
testPopShouldThrowEmptyStackException
}
Notice something missing here? We did right away once we had this structure. We had an object in a known state (an empty stack), and we were making assertions about the initial state and then how the empty stack responded to push() and pop(). Well what about peek()? How should an empty stack behave when you send it the peek() message?
I'm sure we could have analyzed all of the tests in the big single TestCase and discovered this, but what would this analysis look like? We likely would have broken the tests down by state... "when the stack is empty we're testing this, that and the other".
Restructuring exposed the missing specification immediately. In the end we found ourselves with four test cases:
EmptyStackTest
FullStackTest
StackWithOneElementTest
StackWithNineElementTests
In each case there was a test method that made assertions about the initial state, and then how the stack in that state responded to push(), pop() and peek().
Admittedly, this did not change our implementation at all in this case. Perhaps that's because we had enough tests to guide us to the right code. Remember, however, that the tests are multi-purpose. In addition to driving the design, they live on as executable specifications - reference documentation that is, by definition, accurate. The result of our experiment really looked like a specification. If you were a developer trying to use this stack and wanted to know what to expect when you try to pop an empty stack, you could very easily find the right test method to answer your questions.
!commentForm
This sounds like a great idea. Does the common setup always have to drive the seperation? If you have specs seperated by behavior rather than object state does this testing ethos work out as well?
Could there be :
PopStackTest
PushStackTest
PeekStackTest
These tests have different setup, however it is easier to read behavior as specifications than state, which implies you understand the behavior well enough to understand the state. For efficency, I agree with the state seperation, however, specs are driven more by the behavioral names. Or does the state imply the behavior?
Could there be :
PopStackTest
PushStackTest
PeekStackTest
These tests have different setup, however it is easier to read behavior as specifications than state, which implies you understand the behavior well enough to understand the state. For efficency, I agree with the state seperation, however, specs are driven more by the behavioral names. Or does the state imply the behavior?
This describes the impression I got when I read about Behavior Driven Development. If I remember correctly, you have Contexts instead of TestCases[?]. I think Contexts map to initial object states.
Do you think the testSizeShouldBeOneAfterPush() should really be on the StackWithOneElementTest[?] instead of the EmptyStackTest[?]?
What I don’t like about this style is all the TestCase[?] classes. And there’s common setup among the TestCases[?] that should be factored out somewhere else as well. So you end up with many classes necessary to test/specify one production class.
Do you think the testSizeShouldBeOneAfterPush() should really be on the StackWithOneElementTest[?] instead of the EmptyStackTest[?]?
What I don’t like about this style is all the TestCase[?] classes. And there’s common setup among the TestCases[?] that should be factored out somewhere else as well. So you end up with many classes necessary to test/specify one production class.
Hi Clint - I've been paying very close attention to the conversation around BDD, so the connection you make is spot on.
I do think that testSizeShouldBeOneAfterPush belongs on EmptyStackTest for the reason that you cite: contexts make to initial object states. You get the object into a known state (empty), do something (push) and compare the results to your expectations. Having testSizeShouldBeOneAfterPush on StackWithOneElementTest would be confusing because the initial state in that context IS a stack with one element. In StackWithOneElementTest, you could have testSizeShouldBeOne if you wanted. There's no hard rule here.
As for the number of TestCase classes exceeding the number of production classes, that doesn't bother me at all. The clarity you gain from having a clearly understood context far outweighs any clutter in the directories caused by the additional classes.
And by all means, if you see duplication in the setups, pull that out to some helper. In this case that could be a StackTestHelper or a StackFactory or something (please, NO BaseStackTest - they usually end up hurting clarity rather than helping).
I do think that testSizeShouldBeOneAfterPush belongs on EmptyStackTest for the reason that you cite: contexts make to initial object states. You get the object into a known state (empty), do something (push) and compare the results to your expectations. Having testSizeShouldBeOneAfterPush on StackWithOneElementTest would be confusing because the initial state in that context IS a stack with one element. In StackWithOneElementTest, you could have testSizeShouldBeOne if you wanted. There's no hard rule here.
As for the number of TestCase classes exceeding the number of production classes, that doesn't bother me at all. The clarity you gain from having a clearly understood context far outweighs any clutter in the directories caused by the additional classes.
And by all means, if you see duplication in the setups, pull that out to some helper. In this case that could be a StackTestHelper or a StackFactory or something (please, NO BaseStackTest - they usually end up hurting clarity rather than helping).
Add Child Page to UsingMultipleFixtures