It's simple:
By learning to think about the "What" __before__ you think about the "How"
In other words, think about what exactly you want to do (interface), and not how you are going to do (implementation)
TDD, as a design tool based on my experience, really helps you look at things from the perspective of the user than the perspective of the encoder. Also, I think TDD helps your mind really think about what exactly you are trying to do, what the expected result, etc.
So, the next time you try TDD, ask yourself what exactly you are trying to do, and just start writing code that expresses your intention.
Example:
Say someone wants you to write them down as a whole adder.
A person who does not have the TDD skill will simply do:
int add(int a, int b) { return a + b; }
Is the above code correct? Of course yes. But this approach, based on my experience, fails when you have a complex recording component. (That's why you asked this question in the first place, I know, I was there before (maybe yet? Lol))
The great thing about TDD is that it forces you to first of all pay attention to the interface (what) of the system, without immediately asking you about the implementation (how).
In other words, if someone asked me to write an adder, I would have something like:
void assertOnePlusTwoEqualThree() { assert( add(1,2) == 3 ); }
Please note that even before I even thought about how add () should work, I already found out a little. That is, I already understood:
- the interface of my adder as inputs and outputs
- first trivial test example (unit test for free!)
then you implement add ().
See, it doesn’t matter if the logic discussed here is so simple to implement. To have TDD thinking, you need to apply it all the time without any exceptions. You have to do this so many times that you no longer think about it. This is just part of how you develop. I can say this because I saw how it happened to me professionally (it took a year of perseverance).
In the same way as if you always do a good job of programming, regardless of complexity, you approach your work in the same way.
Finally, I think TDD is comparable to a "code sketch." That is, you begin to check whether the interface works well for each scenario (test case). Therefore, it is ok if you end up changing the interface, names, etc. See what I learned that many times design is just for understanding the problem. TDD is one of the tools that allows you to do this.
The human mind, IMO, is easier to work with specific examples (test scripts / scripts), rather than abstract thinking (how to implement something). With test cases, it allows your mind to gradually learn about the problem that you are trying to solve at hand.
This is normal not to know (going back to the "traditional" way ... relax, let your mind adjust). This is normal if it is not perfect (writing the implementation code is all right ... your brain is just trying to figure it out). Just keep trying, keep reading, keep coding, but never give up! =)