Skip to content

Commit 0799f53

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

File tree

1 file changed

+94
-0
lines changed

1 file changed

+94
-0
lines changed

docs/pages/principles/testing.md

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

0 commit comments

Comments
 (0)