4 ms·
The author defines two types of TDD: "weak TDD" and "strong TDD". I'd argue there's another, though I'm not sure what to call it -- "Pragmatic TDD" perhaps? Wha
by gregmac 4y ago
The author defines two types of TDD: "weak TDD" and "strong TDD". I'd argue there's another, though I'm not sure what to call it -- "Pragmatic TDD" perhaps? What I care about is having unit tests that cover the complicated situations that cause bugs. I think one of the main problems with TDD is its proponents focus so much on the process as opposed to the end result.
The way I practice "pragmatic TDD" is to construct my code in a way that allows it to be tested. I use dependency injection. I prefer small, static methods when possible. I try not to add interfaces unless actually needed, and I also try to avoid requiring mocks in my unit tests (because I find those tests harder to write, understand, and maintain).
Notably: I explicitly don't test "glue code". This includes stuff in startup -- initializing DI and wiring up config -- and things like MVC controllers. That code just doesn't have the cost-benefit to writing tests: it's often insanely difficult to test (requiring lots of mocks or way over-complicated design) and it's obvious when broken as the app just won't work at all. Integration or UI automation tests are a better way to check this if you want to automate it.
I strive to just test algorithm code. Stuff with math, if/else logic, and parsing. I typically write the code and tests in parallel. Sometimes I start writing what I think is a simple glue method before realizing it has logic, so I'll refactor it to be easy to test: move the logic out to its own method, make it static with a couple extra parameters (rather than accessing instance properties), move it to its own class, etc.
Sometimes I write tests first, sometimes last, but most often I write a few lines of code before I write the first tests. As I continue writing the code I think up a new edge case and go add it as a test, and then usually that triggers me to think of a dozen more variations which I add even if I don't implement them immediately. I try not to have broken commits though, so I'll sometimes comment out the broken ones with a `TODO`, or interactive rebase my branch and squash some stuff together. By the time anyone sees my PR everything is passing.
I think the important thing is: if you look at my PR you can't tell what TDD method I used. All you see is I have a bunch of code that is (hopefully) easy to understand and has a lot of unit tests. If you want to argue some (non-tested) code I added should have tests, I'm happy to discuss and/or add tests, but your argument had better be stronger than "to get our code coverage metric higher".
Whether I did "strong red-green-refactor TDD" or "weak TDD" or "pragmatic TDD" the result is the same. I'd argue caring about how I got there is as relevant as caring about what model of keyboard I used to type it.