Thoughts on Unit Testing

2 minute read

Writing Unit Tests Saves You Time in the Long Run

For those who don't often write unit tests, the common thought is usually: "Writing tests is too time-consuming." In the short term, this might seem true, but unit tests save you a lot of time in the future.

Without tests, refactoring can be a daunting task. You have to carefully test every scenario manually. If you're not careful, you might introduce new bugs during the refactoring process. Moreover, you need to ensure that your changes don't break other related functionalities. This alone can consume a significant amount of your time.

However, with adequate unit test coverage, everything becomes much easier. You can modify your code creatively, and to check for potential issues, you just need to run `mix test` and wait a little. Any related errors will show up.

So, unit tests do not waste your time or reduce your development efficiency. They save you time in the long run and provide confidence and security during development.

Practicing Test-Driven Development

TDD (Test-Driven Development) is a software development methodology introduced by Kent Beck. In a TDD workflow, instead of directly modifying code, you first write the test cases needed for the change.

The basic TDD workflow is as follows:

  • Write the test case (even if the module referenced in the test doesn't exist yet).

  • Run the test case and let it fail.

  • Write the simplest code (focus on functionality, not code cleanliness).

  • Run the test case and let it pass.

  • Refactor the code to remove duplication and make it cleaner.

  • Run the test case to validate the refactor.

  • Repeat the entire process.

TDD is an effective workflow that maximizes the potential of unit tests to drive design and helps you write better code. While you don't need to strictly follow this process, trying TDD can help you discover the development style that best suits you.

If Writing Tests is Hard - Refactor the Code

When I first started, without practicing TDD, I would usually implement the functionality first and then write the tests. A recurring issue was that the tests were difficult to write.

I found it hard to cover all aspects of my functions with tests, or my tests would end up being awkwardly written to cover all branches. With my growing development experience, I realized that this situation often indicates poorly written code. Instead of adjusting tests to fit your code, refactor your code to make it easier to test. Good code shouldn't have this issue.

Think of your test files as a strict reviewer that won't go easy on you. If your code is bad, the tests will loudly tell you. It might involve redesigning or improving abstractions. Try to optimize your code - it's a fun part of the process.