Skip to content

Instantly share code, notes, and snippets.

@danhodge
Last active September 21, 2023 14:35
Show Gist options
  • Save danhodge/07093b21b3d0a88af782d82c3b2b8e7c to your computer and use it in GitHub Desktop.
Save danhodge/07093b21b3d0a88af782d82c3b2b8e7c to your computer and use it in GitHub Desktop.
Pytest Notes
# filter tests by name (works for standalone test functions or method names on a test class)
#
# pytest <test_file.py> -k test_foo
# pytest <test_file.py> -k 'test_foo or test_bar'
# run the same test multiple times with different parameters
@pytest.mark.parametrize("p1, p2", [(1, 2), (3, 4), (5,6)])
def test_foo(p1, p2):
assert bar(p1) == p2
# capture the exception
def test_foo():
with pytest.raises(SomeError) as exc_info:
do_it()
assert exc_info.value.attr == "expected"
# mocking (from mock import Mock)
# create mock that responds to everything
mock = Mock()
# create mock that only responds to the Clazz API
mock = Mock(spec=Clazz)
# stub an attribute
mock.attr_name = 'foo'
# stub a return value
mock.method_name.return_value = 'bar'
# stub an exception
mock.do_something.side_effect = RuntimeError("Problem")
# stub out a context manager
mgr.return_value.__enter__.return_value = 'foo'
mgr.return_value.__exit__.side_effect = RuntimeError("Problem")
# @patch a function that was imported into a module
from foo.bar import func
class ClassUnderTest:
def foo(self):
return func(2) - 12
@patch("my.module.file.func")
def test_foo(self, mock):
mock.return_value = 12
assert ClassUnderTest().foo() == 0
# validate some (but not all) of the parameters to mocked function
mock.assert_called_once()
assert mock.call_args_list[0].args[1] == "foo" # validates the second argument from the first invocation
assert mock.call_args_list[0].args[2] == "bar" # validates the second argument from the first invocation
assert mock.call_args_list[0].kwargs["foo"] == "bar" # validates the foo kwarg from the first invocation
# if you don't have any kwargs (or don't care about kwargs), you can do this instead
assert mock.call_args[0][2] == "bar" # validates the second argument from the first invocation
# match any parameter
mock.assert_called_once_with(mock.ANY)
# custom matcher
class CustomMatcher:
def __init__(self, expected):
self.expected = expected
def __eq__(self, actual):
return self.expected == actual.values[0].prop_name
mock.assert_called_once_with(CustomMatcher(expected_value))
# using a custom matcher in a test spy verification
mock.assert_has_calls(call('one', CustomerMatcher('foo')), call('two', CustomMatcher('bar')))
# order independent verification of a mock with multiple calls
mock.some_async_method.assert_has_calls([call(arg1), call(arg2), call(arg3)], any_order=True)
# mock a method to return different values across multiple invocations
def fn(arg):
if arg == 1:
return value1
else:
return value2
mock.some_method.side_effect = fn
# mock a generator method
mock.some_method.return_value = iter([1, 2, 3])
# mocking out an instance method on a class
class Foo:
def __init__(self):
self.value = 12
def do_it(self, x):
return self.value + x
from mock import patch
with patch.object(Foo, "do_it", return_value=123) as mock_do_it: # can also be used as a decorator
f = Foo()
r = f.do_it(12)
assert r == 123
mock_do_it.assert_called_once_with(12)
# mocking out onject initialization (not working - python3 only)
from unittest.mock import Mock, patch
import decimal
with patch("decimal.Decimal.__new__") as mock_new:
mock_decimal = Mock(spec=decimal.Decimal)
mock_decimal.sqrt = -3
mock_new.return_value = mock_decimal
assert decimal.Decimal("123").sqrt == -3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment