Skip to main content

Writing tests first saves time and money later on

Published on

The TalkingDrupal podcast had Alexey Korepov on to talk about Test Driven Development. Alexey has written the Test Helpers module, a development package that provides many useful utility tools for writing unit tests for your Drupal code.

If you aren't familiar with test-driven development, the practice is to write tests before you write your code. Create your tests to define your expected output given certain inputs. Then, you write your code. I prefer this methodology as I write code to meet my given solution. Once the test passes, I find ways to refactor and clean up my code. So long as my tests stay green, I know I'm on the right path.

Not everyone is a fan of test-driven development or even tests. One complaint I have heard is that it "slows me down." Sometimes, it is best to slow down because it makes you think. There is truth to "slow is steady, steady is fast," derived from the US Navy SEALs: "Slow is smooth, and smooth is fast." We've all jumped headlong into a problem and warped out an hour later on the other side, wondering if we've actually been productive or made the problem worse. I have found following test-driven development puts me in check. I also prefer the speed of feedback a test gives me over manual testing.

You may say, "speed, but tests run so slow." They do if you're using Drupal's Functional and FunctionalJavascript test suites. Those tests use a full Drupal install, and the latter also runs a headless browser. I prefer always to use Kernel tests and fall to Unit tests when I don't need a minimal Drupal installation. The Google Tag module is probably the best public example I have for my testing approach: https://git.drupalcode.org/project/google_tag/-/tree/2.0.x/tests/src/Kernel?ref_type=heads. Four years ago, I wrote Do you need a Functional test for that? highlighting that we can test controllers and their responses from within Kernel tests, just like Symfony and Laravel do in their own tests.

This allows for faster development and debugging than manual testing.

Another use case to think of is a bug report. When you receive a bug report, is it your first instinct to write a test or manually reproduce it? Based on the bug report's information, I try to write a test first. If the bug report isn't succinct enough to write a test, it probably doesn't have enough details for me to manually reproduce – or worse, I will fill the gaps with my own assumptions on how the bug was triggered. This extends the feedback loop and wastes time. If the bug report is a false positive, we now have extra test coverage! If it is a real bug, great! We now have a failing test against which to work.