In the past I have been guilty of not testing enough (who hasn't). I went through a phase of code first and test as late as possible, then code first and write tests - the old idea of unit testing. However this always ends up in testing getting left behind as time pressures push the demand towards just getting the code done.
I was then introduced to Test Driven Development, it made sense, but I struggled with it. Even when I thought I was doing it right I would find as time pressures increased that it would slip, and it wouldn't be long before I was back in the old style of unit testing.
I used to grapple with design, either in my head or on paper, then write a test against that design and then try and code that design. The problem being, that the design was never quite right. Some requirement was always missed or not considered, or even worse non-required features would slip in because they seemed like a good idea. Going back and changing that design, meant going back and changing the tests.
The other problem was that the tests were always biased towards that vision instead of testing the requirements, increasing testing became a matter of just adding more and more tests, a lot of them testing the same thing and of dubious value.
So as a result things never flowed, there was always a discontinuity - and that leads to turbulence and drag. Drag means wasting energy, there in the temptation to ditch that perceived drag, the testing, instead of the real drag, a bad design.
I then had the pleasure of working on a project with some really focused guys (Jim, Pat, Alistair amongst others), those who knew what TDD really was, and how to really do it, how to use the tools in the right ways. To them it was just natural, everything flowed, there was no drag.
The 'click' was the realisation was not that I didn't know how to write unit tests, nor was it that the test had to be written before the code, but the test had to be written before the design had crystallised.
Now, I am not saying that the secret is no design. Indeed design is very important, but now I tend to do enough design when and as its needed. The final design emerges as I test, code, test, refactor, test.