How should we test our systems or code, what are good things to test, what can we skip. Keep testing simple and keep these other ideas in mind.

Automated Testing

Write unit tests, integration tests, and end-to-end tests to ensure that your code behaves as expected. This helps catch and fix bugs early, catching bugs early means saving lots of time in testing later on.

Automating tests means running them regularly in the local environment but also enforcing them when creating pull requests and merging code into main branches or production.

Test early (Shifting Left)

The earlier we test the more efficient we are by not letting errors propagate through our workflow.

Testing early avoids going through multiple review steps, avoids having to re-start work, avoids building on bad code or rewriting code.

Keep Testing Simple

Rely on basics, unit testing used right, with proper dependency injection can cover a lot of our own code. Focus your testing on checking inputs and outputs.

Integration testing is good, but since they typically involve other systems like, databases or 3rd party systems they can be slow. Limit the exposure to third party systems. We’re focusing on our code and our system, we don’t need to check that our email server is running - that only slows down our testing and we start ignoring it.

Screenshot testing is helpful in some cases but can be slower to run and easily break for example when updating operating systems, use with care similar to integration testing.

Testing goals

A minimum of 60% of all new code should be covered by tests, but aim higher. It’s quite common to have code bases which require 80% or higher test coverage.

Adding tests to old code is good, but not typically a goal in itself, instead add tests when refactoring old code.

Use test analysis tools as part of automated testing both locally and when creating pull requests. Set hard limits code coverage (percentages) on pull requests.

Write tests first (TDD)

Test Driven Design helps us think about testing first. It’s not complicated, here are the steps:

  1. Start by writing a test for the code you need. Focus on testing input and output of your function or class.
  2. Run the test to see that it turns red.
  3. Now implement the actual code you need to pass the test.
  4. Run the tests again and see it turn green.
  5. Refactor and Repeat!

Testing first is super powerful for managing bugs. If you get a bug report, first write a test which reproduces the bug, then fix the code.

What to test

Here’s a list of things that are good to test, but it is not a complete list of everything that you should think of.

  • Happy Path: your test should cover the expected use and result of your code.
  • Edge cases and negative tests; what happens if the input is extreme or broken?
  • Error handling and exceptions. Does your code need to handle bad situations?
  • Focus on testing input and output of functions and classes.
  • Test the boundaries, what happens if an array is empty or if you get a bad array index?
  • Security Testing: evaluate the safety of your code, e.g. do we clean SQL injections or XSS attacks?

What NOT to test:

  • Don’t test external systems, databases, email servers etc unless your code is directly responsible for those systems.
  • UI layout or styling can be hard to test and easily changes. Consider carefully before testing these.
  • Keep It Stupid Simple: Don’t overdo the tests, if something is super obvious or safe, maybe we don’t need to test that code.