Testing

This section covers writing tests for the h codebase.

Getting started

Sean Hammond has written up a guide to getting started running and writing our tests, which covers some of the tools we use (tox and pytest) and some of the testing techniques they provide (factories and parametrization).

Unit and functional tests

We keep our functional tests separate from our unit tests, in the tests/functional directory. Because these are slow to run, we will usually write one or two functional tests to check a new feature works in the common case, and unit tests for all the other cases.

Using mock objects

The mock library lets us construct fake versions of our objects to help with testing. While this can make it easier to write fast, isolated tests, it also makes it easier to write tests that don’t reflect reality.

In an ideal world, we would always be able to use real objects instead of stubs or mocks, but sometimes this can result in:

  • complicated test setup code
  • slow tests
  • coupling of test assertions to non-interface implementation details

For new code, it’s usually a good idea to design the code so that it’s easy to test with “real” objects, rather than stubs or mocks. It can help to make extensive use of value objects in tested interfaces (using collections.namedtuple from the standard library, for example) and apply the functional core, imperative shell pattern.

For older code which doesn’t make testing so easy, or for code that is part of the “imperative shell” (see link in previous paragraph) it can sometimes be hard to test what you need without resorting to stubs or mock objects, and that’s fine.