From Testing Reluctance to Testing-First

|

We all know that ‘Wait, what did I just break?’ moment when coding, right? It’s like a right of passage for developers. But what if I told you it’s even scarier after merging to production? Trust me; it happened. This is the story of our journey from software chaos to a testing-first mentality.

The Early Days

When our development team first formed, we were tasked with a section of the application that none of us had previously worked on. To make matters more challenging, there was a lack of documentation and minimal pre-existing tests to provide a safety net.

Initially, we heavily relied on manual quality assurance (QA) to validate our work and identify bugs. It seemed like a practical choice, but the limitations soon became evident. We merged what appeared to be an improved code refactor, only to discover that it had unexpectedly affected a different feature within the application. We only found out after the changes had already gone live on production. It was a turning point that propelled us on a journey towards a more reliable and testing-centric approach to software development.

Starting Small

When we recognized the need to shift our approach, we initiated a small but significant change – the introduction of unit tests. These weren’t the all-encompassing tests you read about in textbooks; they were small, focused, and manageable.  Yet, they marked the beginning of a significant change. The benefits were apparent when refactoring code, we were able to quickly see our refactor broke existing business logic. With each test we wrote, the confidence in our code quality grew. No longer were we blindly relying on manual testing to catch issues; we had a safety net of automated checks in place, catching issues before they could wreak havoc.

Test-Driven Development as a Learning Tool

While Test-Driven Development (TDD) is not a new concept, and opinions on the practice may differ, I think it’s fair to highlight that it can serve as an invaluable learning tool. Especially for developers who are new to the testing landscape, it can help them get into the habit of thinking about and writing tests – an endeavor that, unfortunately, is often shrugged off.

Here’s how it works:

  • Write the Test First: prior to coding, describing its intended functionality. It should fail due to the absence of code.
  • Write Just Enough Code: to make the test pass – no more, no less.
  • Refactor as Needed: run the test suite after each refactor to ensure no existing functionality is broken.

What’s great about TDD is that it provides immediate feedback. If a test fails, you know something’s wrong, and you can pinpoint the issue before it spirals into something more complex. It’s also a way for new testers to practice writing tests that are tightly aligned with the code’s functionality. It ensures every piece of code has a corresponding test, and vice versa. It encourages developers to think deeply about requirements and edge cases, resulting in more reliable software.

Ping Pong Pair Programming for Collaboration and Skill Sharing

Building on the principles of TDD, Ping Pong Pair Programming (PPPP) can be leveraged as an invaluable knowledge sharing practice. It’s an excellent way for experienced testers to pair up with new testers and show them the ropes, thus further promoting mentorship within the team.

Here’s how it works:

  • Developer A writes a failing test.
  • Developer B writes only enough code to make the test pass.
  • Developer B writes the next test.
  • Developer A writes only enough code to make the test pass.

Continue until Developer A and Developer B both agree that there are no more tests for the unit they are currently working on. Either developer can refactor the code only if all tests stay green. Practicing this can be immensely valuable for new testers as they gain hands-on experience while learning from their peers. It’s a dynamic approach that accelerates the adoption of testing practices and fosters a culture of collaboration.

Fast Forward to Today

Today, none of our Pull Requests (PRs) get merged without accompanying tests. It’s become second nature. We’ve extended our testing arsenal to include not only unit tests, but also integration and end-to-end (e2e) tests. These tests play a vital role in safeguarding our codebase, catching regressions and ensuring that our features work as intended.

Our journey wasn’t just about writing tests; it was about cultivating a testing culture within our team. Our shift from testing reluctance to a testing-first mentality has been a significant leap forward. We’ve seen firsthand how small changes in our approach can lead to substantial improvements in code quality, reliability, and developer confidence.

I want to leave you with this thought: whether you’re new to testing or looking to enhance your current practices, remember that the journey starts with a single step. Start small, prioritize testing, gain confidence, and evolve your testing practices over time. You’ll find that testing isn’t just a task; it’s a mindset that elevates your development game.