views:

204

answers:

5

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?

A: 

I would build a set of units for the most independent/lowest-level functionality. Once you have those classes passing every test, you can move on, assuming they will work when called by more dependent functionality.

A: 

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.

JohnIdol
+7  A: 

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:

  1. 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.
  2. How do I prepare the object for use? oven.turnOn(int degrees) sounds good, I'll do that. How do I check it? Better make oven.getTemperature(). There's an obvious test.
  3. Okay, oven is now hot enough and I want to bake my Pie. For that I need oven.bake(Pie p) so I'll make that. But now what? I want to check if the pie is ready but rather than having oven.isPieReady() I think that oven.pastryStatus() which returns things like "nothing in oven", "raw", "almost done", "cooked" and "charred" sounds good and in general should be more extendable than oven.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.

Esko
+1 nicely done, sir
annakata
What happens if you put Cat c in oven? what about putting in a "charred" pie, or a pie not ready for the oven? Limit and invalid input tests are important.
Tester101
Those are individual tests refining the specs, for example testCantBakeACat() :) But yes, limit and invalid input tests are one of the most important ones out there and shouldn't be missed.
Esko
I thought the point was more which classes to write unite tests for first and then which methods for such classes
JohnIdol
+1  A: 

When faced with a list of tests to implement, you'll have categories

  1. Trivial to test but you're sure you can get this done.
  2. Non-trivial but you're reasonably confident of getting it done.
  3. 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.

Gishu
Interesting. In my own experience I found the trivial tests quite effective in turning up a lot of bugs for minimal effort, but good food for thought. +1
peterchen
I'm not saying 'ignore the trivial tests'.. all 3 categories are needed. :) Just the order of taking them on. Category 2 helps clear the 'fog of war' (to use a gaming term) to a greater extent while at the same time not letting you bogged down or stuck with TDD Coder's block.
Gishu
+1  A: 

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.

Brian Rasmussen