Test-first development is the process of writing your test code before your implementation code. I have always felt this was the ideal approach but NOT necessarily the pragmatic approach to development. The thought of writing a full test before writing the code was essentially writing the specifications first and feels almost like a micro-waterfall approach to development.
I attended a couple of talks recently that made me realize that test-first is not necessarily about writing the entire test case before the implementation. Rather, you can take a very iterative approach. The idea is that you write just enough of the test code to get your implementation code to fail followed by just enough implementation code to get it to pass. Then you write just enough test code to get your implementation code to fail again and repeat the process until your test and implementation code is complete.
To be more specific, the first line of test code could simply be calling a method that does not exist in your implementation. This will cause a compile error (for those of us on stronger typed languages). Then you write just enough code to get the compile error to go away. Some of you may be thinking that compiling is not unit testing which is true but it is a form of testing nevertheless.
The second line of test code could simply be an assertion that the returned object is not null. This should result in a test failure. To pass this test, you can simply return a dummy object. Now obviously your implementation code should not be returning dummy objects. So, the third line of test code could compared the return value with an expected value which should cause the test to fail since it was initially returning a dummy value. Then at this point you would modify the implementation method to return the appropriate value dummy object.
Note that this approach breaks up your test into finer grain assertions. Had you write the test first, you would probably have never thought about performing an assertNull which would aid in debugging. In more complex methods, there would probably be many forgotten assertions. This finer grain approach also makes the test easier to write since we are taking a small bite at a time.
I have been using this approach for the last few months and this has actually become my preferred means of development. In fact, I now get that dirty feeling when I write tests after implementation, much in the same way a test-infected person feels dirty pushing code without unit tests.