Emanuel Schmid
July 5, 2023
We do these things
not because they are easy,
but because we thought
they were going to be easy
Let’s assume we have written a linear equation solver
As part of some software that we want to use.
Add a main function:
Run the code:
Makes sense: 2 * -2.5 + 5 = 0
✅
What happened?
Test the entire bicycle.
Test a single gear.
solvers.py
test_solvers.py
No output, everything OK (Python 3).
Not OK (Python 2.7).
Software development is complex.
test everything that you don’t want to break.
Tested/able code is, i.g, better code
Unit Testing is not language-specific.
xUnit Frameworks (e.g.)
unittest
, Pytest
test_that
, tinytest
googletest
, Cput
JUnit
, TestNG
solvers.py
test_solvers.py
Start runner: python -m unittest discover
=================================================
FAIL: test_solves_general_linear_equation (...)
Checks solution for k = 2 and d = 5.
-------------------------------------------------
Traceback (most recent call last):
File "(...)", line 8, in test_(...)_equation
self.assertEqual(result, -2.5)
AssertionError: -3 != -2.5
-------------------------------------------------
Ran 1 test in 0.000s
Adapt for integers.
Restart runner: python -m unittest discover
.
-----------------------------------------------
Ran 1 test in 0.000s
OK
solvers.py
Add test to TestCase
.
Restart runner: python -m unittest discover
..
-----------------------------------------------
Ran 2 tests in 0.000s
OK
k=0
and d=0
?Verified Documentation.
Use suitable assertions.
assertAlmostEquals
for floatsassertTrue(a == b)
Write your tests first. (Clean Code)
Prepare for 1000s of tests.
Minimal maintenance.
Rerun to locate bugs.
Do the work once.
Write tests as soon as possible.
(Actually - nothing speaks against writing
tests ahead of the unit.)
Given-When-Then Pattern. (Clean Code)
At last
test_solve_linear_equation.py
That’s why I chose 5
and 2
.
We could do the same thing here.
We use a nontrivial test fixture.
No need to copy and paste.
Use Fixtures.
You will be facing common challenges.
Break the vicious circle by adding integration tests.
I can’t test hidden/private code.
No problem, everybody else can’t, either.
I can’t test every combination of inputs.
Test until you are confident that your code works.
I don’t understand what a function does.
No problem, use unit testing.
The function has 20 parameters.
Unit testing tests atomic units.
The function needs to read a file.
No problem, there are tools for that: ‘Mocking’
Choose a return value yourself (Python 3).
mockery
package, with_mock
The function writes to a file.
No problem. Just make sure
tempfile
The function creates an image
import pandas as pd
def plotting_function(image, data):
pd.DataFrame(data).plot().get_figure().savefig(image)
Yes, problem. 😱
Run your tests with every commit
And get notified if any of them fails.
GitHub Actions
Build Pipelines
.gitlab-ci.yml