I currently write a lot of python and C++. Although I religiously unit test my C++ code, I’m a bit ashamed to say that I haven’t had much experience with python unit testing until recently. You know how it is - python is one of those interpreted languages, you mostly use it to do quick hacks, it doesn’t need tests. Until you’ve written your entire D-Bus service using python, and every time you make a code change a literal python appears on the screen to crash your computer. So I’ve started writing a bunch of tests and found (as expected) a tangled mess of dependencies and system calls.
In many C-like languages, you can fix most of your dependency problems with The Big Three: mocks, fakes, and stubs. A fake is an actual implementation of an interface used for non-production environments, a stub is an implementation of an interface returning a pre-conceived result, and a mock is a wrapper around an interface allowing a programmer to accurately map what actions were performed on the object. In C-like languages, you use dependency injection to give our classes fakes, mocks, or stubs instead of real objects during testing.
The good news is that we can also use dependency injection in python! However, I found that relying solely on dependency injection would pile on more dependencies than I wanted and was not going to work to cover all my system calls. But python is a dynamic language. In python, you can literally change the definition of a class inside of another class. We call this operation patch and you can use it extensively in testing to do some pretty cool stuff.
Code Under Test
Let’s define some code to test. For all of these examples, I’ll be using python3.5.2 with the unittest and unittest.mock libs on Ubuntu 16.10. You can the final versions of these code samples on github.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
These are two simple classes (and a custom
Exception) that we’ll use to demonstrate unit testing in python. The first class,
Worker, will work a maximum of 40 hours per week before picketing it’s corporation. Each time
work is called, the
Worker will work a random number of hours. The
Boss class takes in a
Worker object, which it uses as it performs
make_profit. The profit is determined by the number of hours worked multiplied by 1000. When the worker starts picketing, the
Boss will hire a new
Worker to take their place. So it goes.
Mocking the Worker Class
Our goal is to fully test the
Boss class. We’ve left ourselves a dependency to inject in the
__init__ method, so we could start there. We’ll mock the
Worker and pass it into the
Boss initializer. We’ll then set up the
Worker.work method to always return a known number so we can test the functionality of
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
To run this test, use the command
python3 -m testtools.run test, where
test is the name of your test file without the
One curiosity here is
unittest.mock.create_autospec. Python will also let you directly create a
Mock, which will absorb all attribute calls regardless of whether they are defined, and
MagicMock, which is like
Mock except it also mocks magic methods.
create_autospec will create a mock with all of the defined attributes of the given class (in our case
work.Worker), and raise an Exception when the attribute is not defined on the specced class. This is really handy, and eliminates the possibility of tests “accidentally passing” because they are calling default attributes defined by the generic
We set the return value of the
work function with
return_value, and we can change it on a whim if we so desire. We then use
assertEqual to verify the numbers are crunching as expected. One further thing I’ve shown here is
assert_has_calls, a mock assertion to verify that
work was called 3 times on our mock method.
You may also note that we subclassed
TestCase to enable running this class as part of our unit testing framework with the special
__main__ method definition at the bottom of the file.
Patching the Worker Class
Although our first test demonstrates how to
make_profit with a happy worker, we also need to verify how the
Boss handles workers on strike. Unforunately, the
Boss class creates his own
Worker internally after learning they can’t trust the
Worker we gave them in the initializer. We want to create consistent tests, so we can’t rely on the random numbers generated by
Worker.work. This means we can’t just depend on dependency injection to make these tests pass!
At this point we have two options: we can patch the
Worker class or we can patch the
randint function. Why not both! As luck would have it, there are a few ways to use
patch, and we can explore a couple of these ways in our two example tests.
We’ll patch the
randint function using a method decorator. Our intent is to make
randint return a static number every time, and then verify that profits keep booming even as we push workers past their limit.
1 2 3 4 5 6 7 8 9 10 11 12
patch, you must describe the namespace relative to the module you’re importing. In our case, we’re using
randint in the
corp.work module, so we use
corp.work.randint. We define the
randint to simply be 20. A fine number of hours per day to work an employee, according to the
patch will inject a parameter into the test representing an automatically created mock that will be used in the patch, and we use that to assert that our calls were all made the way we expected.
Since we know the inner workings of the
Worker class, we know that this test exercised our code by surpassing a 40-hour work week for our poor
Worker and causing the
WorkerStrikeException to be raised. In doing so, we’re depending on the
Boss implementation to stay in-sync, which is a dangerous assumption. Let’s explore patching the
Worker class instead.
To spice things up, we’ll use the
ContextManager syntax when we patch the
Worker class. We’ll create one mock
Worker outside of the context to use for dependency injection, and we’ll use this mock to
WorkerStrikeException as a side effect of
work being called too many times. Then we’ll patch the
Worker class for newly created instances to return a known timesheet.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
After the first
Worker throws a
WorkerStrikeException, the second
Worker (scrub) comes in to replace them. In patching the
Worker, we are able to more accurately describe the behavior of
Boss regardless of the implementation details behind
A Non-Political Conclusion
I’m not saying this is the best way to go about unit testing in python, but it is an option that should help you get started unit testing legacy code. There are certainly those who see this level of micromanaging mocks and objects as tedious, but there is be benefit to defining the way a class acts under exact circumstances. This was a contrived example, and your code may be a little bit harder to wrap with tests.
Now you can go get Hooked on Pythonics!