Skip to content

Commit c9ce440

Browse files
committed
docs: Add outline of testing page to principles
1 parent 06352e2 commit c9ce440

File tree

1 file changed

+73
-0
lines changed

1 file changed

+73
-0
lines changed

docs/pages/principles/testing.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
## Unit Tests
2+
3+
### Advantages of unit testing:
4+
* Unit tests ensure that the code, as written, is correct, and executes properly.
5+
* They communicate the intention of the creator of the code,
6+
how the code is expected to behave, in its expected use-case.
7+
8+
* Writing unit tests can reveal weakensses in our implementations, and lead us to better design decisions:
9+
* If the test requires excessive setup, the unit may be dependent on too many external variables.
10+
* If the test requires many assertions, the unit may be doing too many things / have too many side-effects.
11+
* If the unit is very difficult to test, it will likely be difficult to understand and maintain.
12+
Refactoring code to make it easier to test often leads us to write better code overall.
13+
14+
### Guidelines for unit testing:
15+
* Unit tests live alongside the code they test, in a /tests folder.
16+
+ It looks like `unittest discover` cannot find TestCase classes within source files.
17+
18+
* Test files should be named `test_{{file under test}}.py`, so that stdlib unittest can find them easily.
19+
20+
* test_.py files should match your source files (file-under-test) one-to-one,
21+
and contain only tests for code in the file-file-under test.
22+
23+
* Consider using the stdlib `unittest.TestCase` and other stdlib tools instead of pytest.
24+
+ Allows running unit tests for diagnostics in production environments,
25+
without installing additional packages.
26+
27+
* Keep it simple! If a test-case requries extra setup and external tools,
28+
It may be more appropriate as an external test, instead of in the unit tests
29+
30+
* Test single units of code! A single Function, or a single attribute or method on a class.
31+
Not the interactions between them. (see mocking and patching)
32+
33+
#### Importing in test files:
34+
* Use relative imports, from the file under test:
35+
```
36+
from ..file_under_test import MyClass
37+
```
38+
+ This ensures that the test file always imports from its source file,
39+
and does not accidentally import from an installed module.
40+
41+
* Only import from the file-under-test, unless absolutely necessary:
42+
* If your source runs `from pandas import DataFrame`
43+
and you need a DataFrame for a test, import it `from ..file_under_test import DataFrame`
44+
NOT `from pandas import DataFrame` in the test file.
45+
+ This has some important implications for mocking/patching to elaborate on.
46+
47+
48+
#### Running unit tests:
49+
* Pytest is great for running tests in your development environments! `pytest {{path/to/source}}`
50+
* stdlib's unittest can be used in environments where pytest is not available:
51+
* To use unittest to run tests in your source folder, from your package root, use `python -m unittest discover --start-folder {{source folder}} --top-level-directory .`
52+
* To use unittest to run tests from an installed package (outside of your source repository), use `python -m unittest discover -s {{module.name}}`
53+
54+
55+
#### Mocking and Patching to Isolate the code under test:
56+
* When the unit you are testing touches any external unit (usually something you imported, or another unit that has its own tests), the external unit should be Patched, replacing it with a Mock for the durration of the test.
57+
58+
* Verify that the external unit is called with the expected input
59+
* Verify that any value returned from the external unit is utilized as expected.
60+
61+
```
62+
@patch(f'{SRC}.otherfunction', autospec=true)
63+
def test_myfunction(t, external_unit: Mock):
64+
ret = myfunction()
65+
external_unit.assert_called_with('input from myfunction')
66+
t.assertIs(ret, external_unit.return_value)
67+
68+
```
69+
70+
* Consider what needs to be mocked, and the level of isolation your unit test really needs.
71+
* Anything imported into the module you are testing should probably be mocked.
72+
* external units with side-effects, or which do a lot of computation should probably be mocked.
73+
* Some people prefer to never test `_private` attributes.

0 commit comments

Comments
 (0)