Python unit testing with Pytest and Mock

Brendan Fortuner
Feb 5, 2016 · 2 min read

My favorite documentation is objective-based: I’m trying to achieve X objective, here are some examples of how library Y can help. The Pytest and Mock documentations include many examples, but the examples are not opinionated. It’s not clear which of 7 ways to achieve my objective is best.

Here are some examples I developed while contributing to the Neuroscience research platform BigNeuron. I’ve renamed the methods for clarity, but the original code can be found here: module and tests. (I’m actually using a wrapper library called Pytest-mock instead of plain Mock).

Directory structure that makes running tests easy


How do I run these tests?

python -m pytest tests/ (all tests)
python -m pytest -k filenamekeyword (tests matching keyword)
python -m pytest tests/utils/ (single test file)
python -m pytest tests/utils/ (single test method)
python -m pytest --resultlog=testlog.log tests/ (log output to file)
python -m pytest -s tests/ (print output to console)

Import statements

import mock 
import pytest
from pytest_mock import mocker
from src.jobitems import manager

Verify sub-method called with specific parameters

def test_update_jobs_fleet_capacity(mocker):
mocker.patch.object(manager, 'sub_method')
manager.sub_method.return_value = 120
manager.sub_method.assert_called_with('somestring', 1, 120)

Mock class in another module

def test_helper_class(mocker):
mocker.patch.object(manager, 'other_class')
manager.other_class.other_method.return_value = 50

Run test multiple times with array of params

@pytest.mark.parametrize('test_type, var1, var2, expect', [
('verify_max_returned', 'apple', 90, 100),
('verify_min_returned', 'orange', 2, 10)
def test_get_size(mocker, test_type, var1, var2, expect):
print("Logging test type for visibility: " + test_type)
assert fleet_manager.get_optimal_size('zone1', var1,
var2) == expect

Verify method is called with any parameter (value doesn’t matter)

manager.update_size(mock.ANY, 'var2', mock.ANY)

Override constant in config file

def test_update_config_val(monkeypatch): 
monkeypatch.setattr(config, 'WEBSITE_URL', '')
assert manager.method_that_uses_config_val() == ''

Override environment variable

def test_update_env_var(monkeypatch): 
monkeypatch.setenv('DOMAIN', 'Devo')
assert manager.method_that_uses_env_var() == 'Devo'

Verify unit test fails

def test_that_should_fail():

Verify test throws exception

def test_that_should_throw_exception():
with pytest.raises(Exception):

Skip test that fails intermittently

@pytest.mark.skipif(True, reason="Method is too slow")
def test_very_slow_method():

Create shared Mock object for all tests in module

def my_fixture():
def test_that_uses_shared_mock_obj(my_fixture):
assert manager.do_something(my_fixture, 'hello') == None

That’s it! I hope these examples help.

UPDATE June 2017: Check out Wil Cooley’s response below. He offers some helpful corrections and points out better ways to accomplish some of the tasks above. Thanks, Wil!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store