Testing Ion¶
Unit tests are hugely important to ensuring that Ion functions as expected. By writing and running tests before deploying every change, we can be much more confident that our new changes don’t break the existing design in Ion. As such, every change that introduces a new feature should also include at least one unit test that corresponding to that feature.
Unit Tests¶
For most modules, the unit tests go in intranet/apps/<module>/tests.py. Currently, the sole exception is eighth, where tests are broken out into several different files under intranet/apps/eighth/tests/. Testing functionality that is useful for multiple tests can be found in intranet/test.
Running Tests Locally¶
Tests should be run inside the Docker development environment. Make sure your Docker containers are running first:
cd config/docker
docker compose up -d
Running Tests with Django’s Test Runner (Recommended)¶
The recommended way to run tests is using Django’s built-in test runner, which is what CI uses:
# Run all tests (this may take some time!)
docker exec intranet_django python ./manage.py test --noinput
# Run tests for a specific app (in this case, polls)
docker exec intranet_django python ./manage.py test intranet.apps.polls --noinput
# Run a specific test file (in this case, auth)
docker exec intranet_django python ./manage.py test intranet.apps.auth.tests.TurnstileFieldTest --noinput
# Run with verbose output, useful for debugging
docker exec intranet_django python ./manage.py test intranet.apps.polls --noinput -v 2
Interactive Testing Shell¶
If you want to run multiple test commands, you can open an interactive shell with Docker:
docker exec -it intranet_django sh
# then inside the container, run tests:
./manage.py test intranet.apps.polls --noinput
Coverage¶
Coverage information is auto-generated at Coveralls <https://coveralls.io/github/tjcsl/ion>. This is useful for finding files with insufficient coverage, so you can focus your test writing more accurately.
Writing Tests¶
Looking at pre-existing tests can give you a good idea how to structure your tests. The IonTestCase class is a wrapper around the standard Django test class. It handles some ion-specific logic, such as mocking out ldap queries. Here is an bare-bones example of the basic layout for a test:
from ...test.ion_test import IonTestCase
class ModuleTest(IonTestCase):
def test_module_function(self):
# Put your tests here
self.assertEqual(1, 1)
Every test should include comments that explain, almost in narrative form, what the test is doing and what the expected results are.
Generally, there are two ways that you test Ion’s code. These are not comprehensive, but should work for most cases.
The first is calling methods directly; the second is making a GET or POST request to the view that you are interested in testing.
The best tests utilize both. After you do either/both of these to call the code, you should use assertions to see if the code behaved as expected.
A list of assertion options can be found here.
A useful tool for making requests to the view is self.client. More documentation on that can be found
here. Alternatively, just search through Ion’s
testing files for examples of using self.client.
Good Testing Examples¶
Ion has lots of tests; the following are examples of very well-written ones. Note the abundance of comments and the attention to detail. Test everything.
Polls - Note the throughness of the checks; assertions are made even when nothing should have changed.
Emailfwd - Note the use of self.client and checking messages generated by the page.
Eighth -
eighthis the most complicated app in Ion and thus has the most varied examples of testing methods to check out.