views:

924

answers:

14

I see lots of TDD practitioners following this cycle:

1) Write your test as if the target objects and API already exists.

2) Compile the solution and see it break.

3) Write just enough code to get it to compile.

4) Run the test and see if fail.

5) Write just enough code to get it to pass.

6) Run the test and see it pass

7) Refactor

What is the advantage of steps 1 and 2? With IDEs like Visual Studio this is really annoying to do, since intellisense jumps all over the place trying to guess methods and attributes that are not there.

I usually start on step 3, having all my methods throwing NotImplementedException, and this seems perfectly fine for me, but maybe I am missing something.

Edit for clarification: this is not a question why I should see a test fail before it passes; that is covered on step 3 onwards, and it makes total sense. My question is why even before that people will call a method on the unit test that doesn't exist in the API (therefore VS will show a red squiggle, or paint the whole method name red etc) and try to compile anyway. For me the fact that VS is telling me that the method does not exist is good enough.

+18  A: 

Then try it by writing the method name first. I find by writing the test first and the methods, it forces me to really think about the API, and I'm free to easily change names without having to worry about code that has already been written. My suggestion would be to try not following the rules, and monitor what happens. If you find that it causes problems, switch back. And if it doesn't, you've got a new way of working now.

Remember also that when you are first learning about things, generally you need a clear set of rules to give you context. As you begin to move from Beginner to more advanced, you'll gain more context and be able to make tweaks. It sounds to me like that's where you are.

Cory Foy
+3  A: 

Seeing it break ensures you have not blundered in your test code and built a working test from the outset.

Also, attempting to "use" an API makes you think about it from a different perspective (that of the API user) which is almost always beneficial. It's important to do this before you attempt to write the API (which will be always from the perspective of an API designer). It's difficult to explain the value of using your own APIs, but the industry term is dog-fooding.

Software Monkey
Thanks, but that's not my question. The thing you mentioned is covered on step 3 onwards, and is certainly mandatory on TDD.
rodbv
@rodv: Dog-fooding will change the way you write code, and I believe it's (one of the reasons) why TDD advocates the test as the very first thing.
Software Monkey
+1  A: 

As to Visual Studio's support for TDD, I agree that intellisense will sometimes get in the way, but you can actually use VS for TDD though it is still somewhat limited. If the test method refers to a non-existing method on the class under test, you can have VS create a stub for you by pressing Ctrl-K, Ctrl-M.

Unfortunately this doesn't work for properties in VS2008, but as far as I can tell from the notes for VS2010 there are lots of improvements in this area in the next version.

Brian Rasmussen
Your first paragraph is covered on step 3 onwards, so it's not what I asked. See my edit for clarification. The tip about creating stub is quite nice though, that makes step 1-2 make more sense alongside Cory Foy's answer.
rodbv
+3  A: 

writing the tests first forces you to decide on the interfaces

that is all.

but that is enough! the interfaces are the box within which you must code; ignore them and just start coding, and you often have to rework your work before you can use it

EDIT: i really should read the question more carefully, the OP was looking for a more specific answer. Here is it: omit step 2 in visual studio, stub the methods/classes instead. There's no reason to be pedantic about following this extended recipe when it is clearly not necessary with the tools one is using.

Steven A. Lowe
+1  A: 

The danger here is that you presuppose your API when writing the first test. One trick is to think about the first test -- perhaps write it on paper or, at least, outside VS -- before you start writing code. Once you know the methods the API will need you can proceed with step 3 and then backtrack (with respect to VS) to step 1 and write the actual test.

I confess that I usually do most of this in my head and start with step (3) as well.

tvanfosson
+12  A: 

I am a TDD practitioner and I think your way of doing it is just fine. Seeing the test fail is the important part, not seeing the code fail to compile.

Try this as a revised sequence:

1) Write your test within a comment as if the target objects and API already exists.

2) Write just enough API code to compile.

3) Uncomment your test code.

4) Run the test and see it fail.

5) Write just enough code to get it to pass.

6) Run the test and see it pass

7) Rinse and repeat...

That way you still get the benefit of thinking test first, rather than implementation first.

Justin Standard
I agree, except that there is value in seeing the code fail to compile (or execute in non-compiled languages), as I describe in my answer.
Rob Williams
I read your answer, and you do have a point. To alleviate that concern, you can add step (1.5) to the above: "Uncomment your code and verify which api methods already exist."
Justin Standard
+2  A: 

Remember, first, that the basic cycle is red-green-refactor. So having the code fail to compile isn't part of the core TDD cycle at all. Having said that, as several people have pointed out, it is useful to consider the API in terms of the test being constructed, and that is where your first two steps tend to come into their own. If you write the API the way you want it to work to make your test simple, you're far more likely to be a happy consumer of the class under test.

Mike Burton
A: 

I agree with the other respondents, I think this is just people who are naive trying to do the "right thing" without thinking. They heard somewhere you should write your tests before you write any code, and they do that literally and try to compile for some reason.

This is related to this answer to another question, use your head first! That's the best best practice.

Adam Bellaire
+10  A: 

The workflow in Eclipse with JUnit (as opposed to Visual Studio with MSTest) is where steps 1-3 make the most sense. Step 3 is then just using Eclipse's Quick Fix functionality (Ctrl-1) to generate the stub code based on the unit test you just wrote.

DoesntExistYet someObject = new DoesntExistYet();
int result = someObject.newMethod("123");
assertEquals(123, result);

The Quick Fix for the first line will automatically create the DoesntExistYet class (letting you go through a wizard first to tweak it) and the next quick fix will create newMethod for you, appropriately figuring out the signature based on how you used it.

So with all that automagicification making things easier, you then move on to the other advantages people have mentioned about being able to spec out your API by how you would use it.

Brian Deacon
Agreed. The quick fix and code generation in Eclipse, in combination with thinking the API through before you write them, make this a clean and powerful approach to TDD.
Bent André Solheim
+10  A: 

I think everyone is missing a critical point--how do you KNOW that the desired method does not yet exist? Writing a unit test that invokes a method that should not exist yet, then watching it fail, verifies that your assumption is true. In a compiled language, it should fail to compile. In a noncompiled language, failed execution may be far quicker than checking the API. In most languages, inheritance and polymorphism may result in a method being present that didn't register in your mental model of the API.

On rare occasion, you may find that the method actually does exist (and IntelliSense may help detect that as well), and you may realize that you need to alter your desired method signature. Or, you may even find that you don't need to write that method at all (maybe you wrote it last week, but forgot).

Certainly you can choose to skip those first two steps, or even dispense with TDD altogether, but those steps did have a purpose. Nevertheless, I agree with the general sentiment that we can always benefit from more description of the rationale behind such steps in any "best practice".

EDIT: From Justin Standard...

Or if you work on a team of developers and didn't personally write the code you are relying on. I think that is a fairly common scenario for most developers.

EDIT: From senfo...

If you're having problems keeping track of which methods have been implemented in base classes, it sounds to me like your inheritance hierarchy is too complex. I still voted you up because I agree that I take more time to verify that a method doesn't already exist if I start with the unit test.

@senfo: Too much complexity in the inheritance hierarchy can certainly occur, but that is a different problem with an obvious solution. Even if your existing code is perfect, it is still valuable to start with a unit test that attempts to invoke a possibly non-existent method, just to quickly prove to oneself that it does not exist. Of course, skipping that step is understandable--I might come back to it in a specific situation where my test is misbehaving (to verify in that particular situation that I am not invoking something other than what I just wrote).

Rob Williams
I see your point, but the odds that you would accidentally write the same method signature as an existing hidden method are low. If there was any variation in the name, then the existing method wouldn't be apparent. Intelisense might help, but ultimately only familiarity with the code will do.
Justin Standard
Actually, I find that the odds are high--I tend to be very consistent with my naming, or I find myself writing a common method that was already present but that I thought I had to provide. Of course, this becomes more likely if you have been away from the code for awhile (returning to old project).
Rob Williams
Or if you work on a team of developers and didn't personally write the code you are relying on. I think that is a fairly common scenario for most developers.
Justin Standard
If you're having problems keeping track of which methods have been implemented in base classes, it sounds to me like your inheritance hierarchy is too complex. I still voted you up because I agree that I take more time to verify that a method doesn't already exist if I start with the unit test.
senfo
+3  A: 

I see many people answering this question have a Visual Studio background. I've used VS myself, and struggled with the same issues you are pointing out with the first two steps of TDD.

If you use more powerful code editing IDE's, like what you have in Java (Eclipse, Netbeans, IntelliJ), the first two steps make more sense. The quick fixes and code generation facilities available there, makes writing a test against a nonexisting class or method the fastest way to create that particular class or declare that particular method; you can just press a button, and the missing class or method is generated for you. Writing a method call or object instantiation is quicker that creating the class or method first, then using them. Once you have called a method, for instance, it's given by the method name and parameter types what the signature of the method should be, so it's easy for the IDE to create them. This is really a wonderful process, and I would not program any other way. Combine this with the advantages of actually working with the api before it exists, your described way of performing TDD makes a lot of sense.

What I am claiming here is also true in dynamic languages where you traditionally will not get any compile errors in the IDE for missing classes or methods. The test will fail, giving you the red bar.

For Visual Studio users, if you want to share this experience, install the Resharper plugin for Visual Studio. This will give many of the same features that are available in Java IDEs.

Bent André Solheim
+1 - I use resharper and it allows me to create non-existent methods. Makes writing the tests first on vs.net tolerable. :)
jop
+1  A: 

I think that using a tool like Resharper will help you get a better feel for TDD. It certainly did for me.

This is because it can auto-generate a lot of the empty classes/methods that you need to stub out as you write them. This helps to not interrupt your flow as you think about how a test should run and how the class/method that you're testing should operate.

Karthik Hariharan
+1  A: 

This is not Test-Driven Development by the book. The "official" TDD process, as described in Beck' "Test-Driven Development: by example" is as follows:

  • Quickly add a test
  • Run all tests and see the new one fail
  • make a little change
  • Run the tests and see them all succeed
  • Refactor to remove duplication
philippe
+1  A: 

As far as I'm concerned "see a red squiggle" == the compiler failed. Remember that the original unit test/TDD manifestos were written without IDEs in mind. Good IDEs were very rare in the java world back then, and as others have noted, dynamic languages still can't determine that a method doesn't exist at compile time, period.

If you're using a language/IDE combination that displays compiles errors immediately, then this counts as a failed compile cycle.

Sean Reilly