Testing¶
This section covers running and writing tests for the h
codebase.
Running the tests, linters and code formatters¶
To run the unit tests (both backend and frontend) run:
make test
To run the functional tests:
make functests
To format your code correctly:
make format
To run the linter:
make lint
For many more useful make
commands see:
make help
Running the backend tests only¶
To run the backend test suite only call tox
directly. For example:
# Run the backend unit tests:
tox
# Run the backend functional tests:
tox -qe functests
# Run only one test directory or test file:
tox tests/h/models/annotation_test.py
tox -qe functests tests/functional/api/test_profile.py
# To pass arguments to pytest put them after a `--`:
tox -- --exitfirst --pdb --failed-first tests/h
tox -qe functests -- --exitfirst --pdb --failed-first tests/functional
# See all of pytest's command line options:
tox -- -h
Running the frontend tests only¶
To run the frontend test suite only, run the appropriate test task with gulp. For example:
make gulp args=test
When working on the front-end code, you can run the Karma test runner in auto-watch mode which will re-run the tests whenever a change is made to the source code. To start the test runner in auto-watch mode, run:
make gulp args=test-watch
To run only a subset of tests for front-end code, use the --grep
argument or mocha’s .only() modifier.
make gulp args=test-watch --grep <pattern>
Writing tests¶
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.