When I realized that writing test-able code resulted in me writing better code regardless of the tests I wrote. Just by ensuring my code is easy to test meant that my code was more focused, organized and well designed.
"Ugh, this massive class is going to be a pain to test. I better break it down into smaller, more composable pieces to make my life easier" => Single Responsibility Principle
"Ugh, I don't want to have to mock out these constructors and static methods. I better pass those dependencies in to make my life easier" => Dependency Inversion Principle
That's S OLI D - 2 out of 5 key design patterns just to make my life easier in the short term. How can you not follow something that gives you that?
TDD also puts strong pressure on the developer to do the L for Liskov's Substitution Principle, because mocking / fakes requires it.
And it even puts some light pressure to do the I for Interface Segregation Principle... depending how deep the code goes into DIP.
Very good points as well!
I'm struggling with linking Open/Closed Principle to TDD. Anyone else want to step up?
They seem orthogonal IMO.
Perhaps we can say that a module that's closed for modification is not going to see as much rewriting of tests. In other words, the need to throw away tests could be a signal that you're not making as much use of inheritance as you could be. (Though perhaps not a useful signal, since if you're modifying the code then it's already obvious that it isn't closed, and there might also be a good reason for that.)
For the open-for-extension part -- if you're inheriting from something, you don't need to touch its tests, so I wouldn't expect any interplay with TDD.
To be honest, I think that the other parts tie in is just due to the fact that TDD is most practical when unit testing small, decoupled modules, and if your code is organized around classes, that mostly implies SOLID. In other words, to the extent that TDD and SOLID correlate, modularity might be a mediating factor.
I say this because I think you can argue that in the FP paradigm, SOLID-like properties are easier to hit just as a matter of course, in which case TDD wouldn't have as much architectural impact. (That said, there's certainly nothing preventing one from writing large, tightly-coupled functions.)
We're a place where coders share, stay up-to-date and grow their careers.
We strive for transparency and don't collect excess data.