Is What I'm Doing TDD?
Does it count as TDD if I make it like this:
- Thinking about tests I write simple small fine-grained functions
- I comment out all code in functions
- I write test, see it fail
- I uncomment one block of code in function to see one test turn green
- if not all code is uncommented: goto step 3
I find, that it brings less anxiety when I put all edge cases in code first. So does it count as TDD?
Why do you care about this question? If I answered, "It’s TDD", what would that change for you? If I answered, "It’s not TDD", what would that change for you?
(I know that when someone else answers this way, it annoys me, even though years later I often realize that I needed that kind of answer. Now I get to do this to others.)
It's Not TDD
No, it's not TDD, but it still sounds quite good! I used to do almost the same thing, but maybe in a different sequence.
- I write the code that I think I need to write, because "the" solution is in my head.
- I figure out how to test it.
- I write a test, make it fail (probably by deleting or changing production code), then make it pass (probably by undoing what I did to make the test fail).
- I remove duplication, if I see any.
- Goto 2 until I can't think of any more tests.
I did this most often when I didn't understand how to test something that didn't exist yet. I also did this often when I thought I knew which code to write, but I couldn't see yet how to drive it from the tests. Often I would do this dance:
- Write the code that I expect to write.
- Delete the code.
- Try to write small tests to drive exactly that code.
- Write just enough code to make the test pass.
- Repeat until the code I test-drive matches the code that I had expected to write.
At first, I wanted to succeed at this last step, but over time, I realized that I wanted to fail at this last step. Failing at it means that I probably wrote less code to solve the problem. I expected to learn to do exactly this by practising TDD! I expected that practising TDD would encourage me to write less code, simpler code, clearer code. By definition, if it only helped me test the code already in my head, then my designs would never improve.
It's Not TDD, But It's Fine
None of this is "really" TDD, because the tests aren't driving the code. I don't mind! I think it becomes TDD as I do step 1 less and less. Slowly, over time, I become more comfortable thinking directly in terms of tests and letting the production code evolve from those tests.
Maybe you want a label to describe what you're doing, because then it becomes easier to talk about. I would call it "test-along programming". You are writing tests along with the production code, so it is not "test-first", and you don’t seem to be refactoring, so it is certainly not "test-driven". I guess it’s simply programming with automated microtests1. It probably helps you find and eliminate mistakes, which probably helps a lot. If you never change your production code design after you write the code in step 1, then it is not test-first or test-driven. I repeat: it's still quite good.
You do this and it reduces your anxiety. I used to do it and it also reduced my anxiety. It sounds good, but it is not TDD.
What will you do now?
PS: Error Cases first
I used to do a very bad job of handling errors in my code. In the old days, I would say, "It's done, except for the error handling". (I was young and foolish.) When I learned about test-first programming, I thought it would help me to remember to do the error handling earlier, so I started writing the error cases before writing the happy paths. This way, I didn't forget the error handling. It sounds just like eating your vegetables before eating dessert. It helped me a lot! My code became much more robust. (The bar was low, so this wasn't such a big achievement.)
Over time, thinking about error cases as tests became a habit, and I didn't need to start with the error cases. I stopped doing this and, instead, focused on the happy paths. I began to think about how to design my system to reduce the number of error paths, especially the number of paths that throw exceptions/raise errors. I found that this also made my code more robust. (This was a slightly more impressive achievement.)
You can start with the error paths, the happy paths, or any path you like. It matters much more to pay attention to your patterns of behavior, notice when you make mistakes, then do things to reduce the chances of making mistakes. It sounds like that's what you're doing, so keep doing it!
Others would say "unit tests", but then we have to argue about what a "unit" is, and I don't argue about that any more.↩