Test Driven Development, commonly referred to as TDD, is the development principle that before writing any code, you should have failing test cases. Though it's been around for a while, it's certainly a buzzword in many developer circles along with "pair programming" and "agile". However, I've never met someone who actually practiced TDD. The idea is that by testing first, you can use tests as a benchmark for success while you're writing the actual functionality.
Sometime last week, I took a ticket to add some fairly standard CRUD (create, read, update, and delete) handlers for managing users and thought this was an excellent opportunity to try TDD for the first time.
The first day, I wrote my tests for some GET routes. We use something called go-mock in our unit tests, which allows you to only focus on testing the function you're writing. Because of this, I found that you still needed to have a good idea of the code you want to write while writing out the tests. In our case, we needed to mock out our calls to an external API. If you asked for my opinion after the first day, I would've told you that I could envision the benefits of TDD for something like end to end testing, where it's a black box and you're only verifying that the output is what's expected from a given input. But for unit testing, it wasn't any different than just writing the code first.
The second day, however, made me change my tune.
As I started writing functions that would need to modify what was returned from that external API, I found myself thinking of edge cases that I certainly would not have thought of while writing the code. These would be cases that I would likely catch while writing tests after the fact and would have to rewrite. Or, potentially, I wouldn't even catch them until it was caught in production! I found it not only made me more aware of edge cases but saved time, as well.
I'm not sure how this would work with a more complicated handler that would need to reach out to many more entities. The principle of TDD seems to be that the test is the gold standard for whether your code works, but tests aren't always perfect (at least mine aren't). I'm looking forward to continuing to use TDD in practice and refining my approach to development.
Do you use TDD? How has it helped you in practice? If you don't use TDD, is there a reason?
Top comments (4)
I like TDD also because of edge cases, because with them you don't focus on feature details, you just iterate over all possible inputs and verifying that all possible outputs are correctly handled. It's very common to forget some rare case, that's why I often create test with failure inside, describing what case should be verified after implementation (usually it's because it's not known what services should be mocked).
That's such a good idea!
TDD is good in order to provide just the minimum code that passes the test, so the test must be a reflect of the reality you're modeling in your code. Said that I try to use it as much as possible because as you said it obligates you to think in the edge cases while writing code first not and avoids you to refactor later when that happens. For the mocks I started using the same lib as you and also mockery, but nowadays I prefer to write my own mocks depending on each case and save me one import
That's a really interesting way to think of it. Like not aiming for perfection but having it serve as a guide for what your code should satisfy.
Interesting about the mocks! Do you have an example of what that looks like? I agree saving imports are nice, but to me the mocking frameworks save so much code while being very robust (allowing you to provide expected inputs and direct the outputs). Though it's very possible that I haven't seen a good hand-written mock!