When you've come up with an overall design / idea for how a part of a system should work, how do you decide where to start when doing TDD, or rather, how do you decide your first test to start with?
These are generic guidelines I find useful for prioritizing unit testing:
1) Identify Boundary Objects (Win/WebForms, CustomControls etc).
2) Identify Control Objects (Business layer objects)
3) Write Unit tests only for control objects public methods invoked by boundary objects. This way you'll be sure you're covering main functional aspects of your app.
You can use these rules to prioritize your unit testing - then if you need to micro-test other stuff you can also do it depending on your needs.
Lets assume I'm coding a class called Oven
to bake my delicious Pie
objects. This is how I step through the unit-test order:
- What do I need to do to instantiate the object? In this case it would most likely be
Oven oven = new Oven();
No test for this one, I suppose. - How do I prepare the object for use?
oven.turnOn(int degrees)
sounds good, I'll do that. How do I check it? Better makeoven.getTemperature()
. There's an obvious test. - Okay, oven is now hot enough and I want to bake my
Pie
. For that I needoven.bake(Pie p)
so I'll make that. But now what? I want to check if the pie is ready but rather than havingoven.isPieReady()
I think thatoven.pastryStatus()
which returns things like "nothing in oven", "raw", "almost done", "cooked" and "charred" sounds good and in general should be more extendable thanoven.isPieReady()
so I'll do that.
And so on and so forth. So, I'll make my tests in order I expect to use the object refining the specification as I go. In the end I usually end up with rather simple yet powerful API which does what I want. After I've unit tested my API, I run coverage on my code to see what I missed and then add extra tests for those.
When faced with a list of tests to implement, you'll have categories
- Trivial to test but you're sure you can get this done.
- Non-trivial but you're reasonably confident of getting it done.
- Non-trivial but difficult-absolutely no clue of getting it done.
In such a scenario, Pick one from the Category2 bucket because it will provide with max knowledge/learning per unit of invested time. Plus it will get you rolling and boost confidence to ramp up to the more difficult Category3 tests.
I think I got this from TDD By Example - Kent Beck. Have a look in case you have the time.. Recommended.
I've been teaching an in-house class on TDD for a while now, and a lot of the participants start off by testing all the error cases. Relevant as they may be, that is not the best way to start imo.
Start by setting up tests, that are easy to implement and still tell you if you are moving in the right direction in regards to the features you're trying to implement. So if you're building a stack make sure that you verify push and pop before testing the error cases.
Remember that the goal of the test is not only to verify behavior, but also to let you use the interface of the type under test. If writing the tests feels wrong, you probably need to change the interface.
A good idea is to do each test case backwards. So start by writing the Assert statement. This is the goal of the verification for this test. Then add whatever steps are necessary to get to the point where you can do what the Assert does.