Tests are run with nose.
Unlike many projects that use nose, tests cannot normally be invoked with the
nosetests
command because it does not perform necessary Django setup.
Instead, tests are invoked using the standard Django convention:
./manage.py test
.
Nose plugins are used for various purposes, some of which are optional and can
be enabled with command line parameters or environment variables. Others are
required by the test environment and are always enabled. Custom plugins are
registered with django-nose via the
NOSE_PLUGINS
setting in
testsettings.
One very important always-enabled plugin applies
patches
before tests are run. The patches remain in effect for the duration of the test
run unless utilities are provided to temporarily disable them. For example,
sync_users_to_es
is a decorator/context manager that enables syncing of users to ElasticSearch
when a user is saved. Since this syncing involves custom test setup not done by
most tests it is disabled by default, but it can be temporarily enabled using
sync_users_to_es
in tests that need it.
Doing a lot of work in the setUp
call of a test class means that it will be run on every test. This
quickly adds a lot of run time to the tests. Some things that can be easily moved to setUpClass
are domain
creation, user creation, or any other static models needed for the test.
Sometimes classes share the same base class and inherit the setUpClass
function. Below is an example:
# BAD EXAMPLE
class MyBaseTestClass(TestCase):
@classmethod
def setUpClass(cls):
...
class MyTestClass(MyBaseTestClass):
def test1(self):
...
class MyTestClassTwo(MyBaseTestClass):
def test2(self):
...
In the above example the setUpClass
is run twice, once for MyTestClass
and once for MyTestClassTwo
. If setUpClass
has expensive operations, then it's best for all the tests to be combined under one test class.
# GOOD EXAMPLE
class MyBigTestClass(TestCase):
@classmethod
def setUpClass(cls):
...
def test1(self):
...
def test2(self):
...
However this can lead to giant Test classes. If you find that all the tests in a package or module are sharing the same set up, you can write a setup method for the entire package or module. More information on that can be found here.
It is important to ensure that all objects you have created in the test database are deleted when the test
class finishes running. This often happens in the tearDown
method or the tearDownClass
method.
However, unneccessary cleanup "just to be safe" can add a large amount of time onto your tests.
The SimpleTestCase runs tests without a database. Many times this can be achieved through the use of the mock
library. A good rule of thumb is to have 80% of your tests be unit
tests that utilize SimpleTestCase
, and then 20% of your tests be integration tests that utilize the
database and TestCase
.
CommCare HQ also has some custom in mocking tools.
- Fake Couch - Fake implementation of CouchDBKit api for testing purposes.
- ESQueryFake - For faking ES queries.
There is overhead to running many migrations at once. Django allows you to squash migrations which will help speed up the migrations when running tests.