10 Unit testing principles

 

This chapter covers

  • The basics of unit testing
  • What makes a good unit test
  • Test doubles, including when and how to use them
  • Testing philosophies

Every time an engineer modifies a line of code there is a risk that they might inadvertently break something or make a mistake. Even extremely small, innocent-looking changes can have bad consequences: “It’s just a one line change” are famous last words before a system crash. Because every change is risky, we need a way to reassure ourselves that the code is working, both initially and whenever it’s modified. Tests are often the main thing that give us this reassurance.

As engineers, we usually concentrate on writing automated tests. This means we write test code that exercises the “real” code in order to check that it’s working correctly. Chapter 1 described how there are different levels of testing, and in particular, how unit testing is the level of testing that engineers typically deal with most often in their everyday coding. We’ll therefore concentrate on unit testing in these two final chapters.

10.1 Unit testing primer

10.2 What makes a good unit test?

10.2.1 Accurately detects breakages

10.2.2 Agnostic to implementation details

10.2.3 Well-explained failures

10.2.4 Understandable test code

10.2.5 Easy and quick to run

10.3 Focus on the public API but don’t ignore important behaviors

10.3.1 Important behaviors might be outside the public API

10.4 Test doubles

10.4.1 Reasons for using a test double

10.4.2 Mocks

10.4.3 Stubs

10.4.4 Mocks and stubs can be problematic

10.4.5 Fakes

10.4.6 Schools of thought on mocking