Advanced tests writing: mocking
When testing, it is useful to replace some part of your application under test to prevent its side effect, like connection to an external service, or make an open call raise an exception.
It is possible to fill your whole hard-drive to get an IOError: No space left on device but it might be better to just modify open to simulate it to test your code.
It is also useful to be able to know how an object has been used or a function been called, the same way as testing what has been printed by your program. In this section you will see mock to test the main function and verify the printed output.
Testing the main function ie calling the sphere script from command line, means:
- Set the command line arguments to some values :
sys.argv
- Verify the printed text : in files
sys.stdout
andsys.stderr
mock provides mainly two important things:
- Mock class which is an object whose behaviour can be configured and that records all action on it.
- patch function that dynamically replaces one function/method/object with a Mock object.
Remark: Explaining mock and applying is too long for this course. So in this section you have to copy paste the code, try it and read afterwards to understand the example. See online documentations when trying it yourself.
- Copy the file:
cp -r raw/fourth gitlabciintroduction/test/main_sphere_test.py
- Add
mock
intox.ini
deps section - Re-run tests:
tox
You can see that we now have 94-97% coverage.
Now open the main_sphere_test.py
file in a text editor and check this:
-
Main function in order to test the main function, the program arguments
sys.argv
should be replaced with different values. Here I am patching it using a context manager (with keyword). It automatically applies and cleans up the patch. -
Mocking outputs Two methods are presented:
- mocking
sys.stdout
with aStringIO object
and get the written string. Here it is also done using a context manager. - mocking the file object and verify write call arguments. It is presented
here for
sys.stderr
by patching insetUp
and cleaning it withpatch.stopall()
in tearDown.
- mocking
Both method are valid, it is just for presenting alternatives.
Some advises on patch:
- When “patching” always verify that the mock has been called by your program, there are so many cases where you can just fail to patch correctly.
- When patching a class, you should use MockClass.return value to get the instance mock.
- The patching path can depend on how the module is imported, see:
http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch.
Read it, try it, and understand this prior testing your application. Basically, when patching an object imported using
import XXXX
it requires absolute patch path. And when an object is imported usingfrom XXX import yyyyy
, it requires patching from the importing module. For every other problems, look at the documentation.
Home | Python Home | << Python Previous - Tox | >> Python Next - Compilation in a docker container