0% found this document useful (0 votes)
4 views

Mocking in python

The document provides an introduction to mocking in Python, focusing on the use of mock objects to isolate functionality during unit testing. It explains various types of test doubles, including mocks, stubs, and fakes, and introduces the unittest.mock library for creating and managing mock objects. Key features of mocks, such as return values, side effects, and assertions, are also discussed, along with practical examples of their usage.

Uploaded by

Hans Jones
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views

Mocking in python

The document provides an introduction to mocking in Python, focusing on the use of mock objects to isolate functionality during unit testing. It explains various types of test doubles, including mocks, stubs, and fakes, and introduces the unittest.mock library for creating and managing mock objects. Key features of mocks, such as return values, side effects, and assertions, are also discussed, along with practical examples of their usage.

Uploaded by

Hans Jones
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 33

Python mocking intro

IoC, DIP, DI and IoC containers


Mock Objects
• In Object-Oriented programming mock objects are defined as
simulated objects

They mimic the behavior of real objects in controlled ways
• The whole point of unit testing is isolating certain functionality (unit)
and test just that. We are trying to remove all other dependencies

Sometimes this can be a database, networking or a file system, so using
mock object we don’t step into the world of integration tests

Other times dependency can be just some other class or function
• A mock object should be used in the situations when

The real object is slow

The real object rarely occurs and is difficult to produce artificially

The real object produces non-deterministic results

The real object does not yet exist (which often is the case in TDD)
Some external dependencies
• External resources are a common source of non-unit test dependencies
– Registry and environment variables
– Network connections and databases
– Files and directories
– Current date and time
– Hardware devices
– And so on…
• External dependencies can often be replaced by test doubles or pre-
evaluated objects
– Test Double (think stunt double) is a generic term for any case where you replace
a production object for testing purposes
– Pre-evaluated objects can be already fetched/computed records in memory arrays
Some various test doubles
• Dummy objects are passed around but never actually used. Usually they
are just used to fill parameter lists
• Fake objects actually have working implementations, but usually take some
shortcut which makes them not suitable for production (an In-Memory Test
Database as Entity Framework Core is a good example)
• Stubs provide canned answers to calls made during the test, usually not
responding at all to anything outside what's programmed in for the test
• Spies are stubs that also record some information based on how they were
called. One form of this might be an email service that records how many
messages it has sent
• Mocks are pre-programmed with expectations which form a specification of
the calls they are expected to receive. They can throw an exception if they
receive a call they don't expect and are checked during verification to ensure
they got all the calls they were expecting
Source: https://martinfowler.com/bliki/TestDouble.html
Test doubles and unittest.mock
• Mocking and related techniques may be easier in Python than in many other
languages
• Dependencies can be substituted without changing class design
• The unittest.mock library provides a core Mock class removing the need to
create a host of stubs throughout your test suite
• After performing an action, you can make assertions about which attributes
and methods were used and arguments they were called with
• You can also specify return values and set needed attributes in the normal
way
• Additionally, mock provides a patch() decorator that handles patching
module and class level attributes within the scope of a test for creating
unique objects
• Mock is relatively easy to use and is designed for use with unittest
• https://docs.python.org/3/library/unittest.mock.html
unittest.mock Quick Guide – return_value and side_effect
from unittest.mock import Mock # import the Mock class # output
from unittest.mock import MagicMock # import the MagicMock class quick_guide_mock1.py
thing.method(): 3
class ProductionClass(): dir(Mock()):
def __init__(self):
['assert_any_call',
pass
# Mock and MagicMock objects create all attributes and methods as you access them and store details 'assert_called',
# of how they have been used. You can configure them, to specify return values or limit what
'assert_called_once',
'assert_called_once_with',
# attributes are available, and then make assertions about how they have been used
'assert_called_with',
thing = ProductionClass()
'assert_has_calls',
thing.method = MagicMock(return_value = 3) # calling method() will return 3 'assert_not_called',
print(thing.method()) # print the return_value 'attach_mock', 'call_args',
'call_args_list', 'call_count',
thing.method(3, 4, 5, key = 'value') # call mock with arguments 'called', 'configure_mock',
thing.method.assert_called_with(3, 4, 5, key ='value') # AssertionError if different than last expected call 'method_calls', 'mock_add_spec',
'mock_calls', 'reset_mock',
print(dir(Mock())) # show all the default mock methods / attributes 'return_value', 'side_effect']
# side_effect allows you to perform side effects, including raising an exception when a mock is called
my_mock = Mock(side_effect = KeyError('foo')) # create a new mock object with KeyError exception dict:
# my_mock() # mock -> side_effect = KeyError exception 1 2 3
values = {'a': 1, 'b': 2, 'c': 3} range:
def side_effect_func(arg): 5
return values[arg]
4
my_mock.side_effect = side_effect_func # mock -> side_effect_func 3
print(my_mock('a'), my_mock('b'), my_mock('c')) # print values side_effect_func(arg)
2
my_mock.side_effect = [5, 4, 3, 2, 1] # change side_effect again
for x in range(0, 5): # side_effect now returns array one by one 1
print(my_mock())
The Mock / MagicMock classes and patch
• Mock is a flexible mock object intended to replace the use of stubs and other test
doubles throughout your code
• Mocks are callable and create attributes as new mocks when you access them.
Accessing the same attribute will always return the same mock
• Mocks record how you use them, allowing you to make assertions about what your
code has done to them
• MagicMock is a subclass of Mock with all the magic methods (__init__, __str__,
__new__, __main__, __name__, ...) pre-created and ready to use

https://docs.python.org/3/library/unittest.mock.html#magicmock-and-magic-method-support

https://rszalski.github.io/magicmethods/

You can use MagicMock without having to configure the magic methods yourself

The constructor parameters have the same meaning as for Mock #@patch('my_mod.ClsName')
patched classes are input

• The patch() decorators makes it easy to temporarily def testFunc(MockCls):


# MockCls == my_mod.ClsName
replace classes in a particular module with a Mock object. assert MockCls is my_mod.ClsName
By default patch() will create a MagicMock for you # call patched class or MockCls.Mthd()
my_mod.ClsName()
MockCls.Mthd = Mock(return_value = ...
Create a Mock object
class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)

Create a new Mock object. Mock takes several optional arguments that specify the behaviour of the Mock object:

• spec: This can be either a list of strings or an existing object (a class or instance) that acts as the specification for the mock object. If
you pass in an object then a list of strings is formed by calling dir on the object (excluding unsupported magic attributes and
methods). Accessing any attribute not in this list will raise an AttributeError.
• If spec is an object (rather than a list of strings) then __class__ returns the class of the spec object. This allows mocks to pass
isinstance() tests.
• spec_set: A stricter variant of spec. If used, attempting to set or get an attribute on the mock that isn’t on the object passed as
spec_set will raise an AttributeError.
• side_effect: A function to be called whenever the Mock is called. See the side_effect attribute. Useful for raising exceptions or
dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns DEFAULT,
the return value of this function is used as the return value.
• Alternatively side_effect can be an exception class or instance. In this case the exception will be raised when the mock is called.
• If side_effect is an iterable then each call to the mock will return the next value from the iterable.
• A side_effect can be cleared by setting it to None.
• return_value: The value returned when the mock is called. By default this is a new Mock (created on first access). See the
return_value attribute.
• unsafe: By default if any attribute starts with assert or assret will raise an AttributeError. Passing unsafe=True will allow access to
these attributes.
• wraps: Item for the mock object to wrap. If wraps is not None then calling the Mock will pass the call through to the wrapped object
(returning the real result). Attribute access on the mock will return a Mock object that wraps the corresponding attribute of the
wrapped object (so attempting to access an attribute that doesn’t exist will raise an AttributeError).
• If the mock has an explicit return_value set then calls are not passed to the wrapped object and the return_value is returned instead.
• name: If the mock has a name then it will be used in the repr of the mock. This can be useful for debugging. The name is propagated
to child mocks.
https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock
Mock class attributes 1
Mocks can also be called with arbitrary keyword arguments. These will be used to set attributes on the mock after it is created:
• assert_called(*args, **kwargs)

Assert that the mock was called at least once.
• assert_called_once(*args, **kwargs)

Assert that the mock was called exactly once.
• assert_called_with(*args, **kwargs)

This method is a convenient way of asserting that calls are made in a particular way.
• assert_called_once_with(*args, **kwargs)

Assert that the mock was called exactly once and that that call was with the specified arguments.
• assert_any_call(*args, **kwargs)

Assert that the mock has been called with the specified arguments.
• assert_has_calls(calls, any_order=False)

Assert that the mock has been called with the specified calls. The mock_calls list is checked for the calls..
• assert_not_called()

Assert that the mock was never called.
# examples
• reset_mock(*, return_value=False, side_effect=False)
mock_obj.assert_not_called()

The reset_mock method resets all the call attributes on a mock object. mock_obj.assert_called_with(10)
• mock_add_spec(spec, spec_set=False)

Add a spec. to a mock
• attach_mock(mock, attribute)

Attach a mock as an attribute of this one, replacing its name and parent.
• configure_mock(**kwargs)

Set attributes on the mock through keyword arguments.
• __dir__()
Mock class attributes 2

Mock objects limit the results of dir(some_mock) to useful results
• _get_child_mock(**kw) # examples

Create the child mocks for attributes and return value mock_obj.return_value = 10
• called mock_obj.side_effect = [1, 2, 3]

A boolean representing whether or not the mock object has been called
• call_count

An integer telling you how many times the mock object has been called
• return_value

Set this to configure the value returned by calling the mock
• side_effect

This can either be a function to be called when the mock is called, an iterable or an exception (class or instance)
to be raised
• call_args

This is either None (if the mock hasn’t been called), or the arguments that the mock was last called with
• call_args_list

This is a list of all the calls made to the mock object in sequence
• method_calls

As well as tracking calls to themselves, mocks also track calls to methods and attributes, and their methods and
attributes
• mock_calls

mock_calls records all calls to the mock object, its methods, magic methods and return value mocks
• __class__

Normally the __class__ attribute of an object will return its type. For a mock object with a spec, __class__ returns
the spec class instead
• Calling
Using Mock objects

Mock objects are callable. The call will return the value set as the return_value attribute. The default
return value is a new Mock object; it is created the first time the return value is accessed (either
explicitly or by calling the Mock) - but it is stored and the same one returned each time

https://docs.python.org/3/library/unittest.mock.html#calling
• Deleting attributes

Mock objects create attributes on demand. This allows them to pretend to be objects of any type.

You “block” attributes by deleting them. Once deleted, accessing an attribute will raise an
AttributeError

https://docs.python.org/3/library/unittest.mock.html#deleting-attributes del mock_obj.my_attr
• Mock names and the name attribute

Since “name” is an argument to the Mock constructor, if you want your mock object to have a “name”
attribute you can’t just pass it in at creation time. There are two alternatives. One option is to use
configure_mock()

https://docs.python.org/3/library/unittest.mock.html#mock-names-and-the-name-attribute
• Attaching Mocks as Attributes

When you attach a mock as an attribute of another mock (or as the return value) it becomes a “child”
of that mock. Calls to the child are recorded in the method_calls and mock_calls attributes of the
parent. This is useful for configuring child mocks and then attaching them to the parent, or for
attaching mocks to a parent that records all calls to the children and allows you to make assertions
about the order of calls between mocks

https://docs.python.org/3/library/unittest.mock.html#attaching-mocks-as-attributes
Basic concepts unittest.mock – return_value
from unittest import mock # import the library
def print_answer(): gentle_intro_to_mock1.py
print(f"print_answer(): {42}")
def print_number(num):
print(f"Number: {num}")
m1 = mock.Mock() # The main object that the library provides is Mock and you can instantiate it without any argument
print(dir(m1)) # show all the default mock methods / attributes
print(m1.some_attribute) # read a non-existent attribute
# Mock objects are callables, which means that they may act both as attributes and as methods. If you try to call
# the mock it just returns you another mock with a name that includes parentheses to signal its callable nature
print(m1.some_attribute())
# The simplest thing a mock can do for you is to return a given value every time you call it.
# This is configured setting the return_value attribute of a mock object
m1.some_attribute.return_value = 42
print(m1.some_attribute())
# Now the object does not return a mock object any more, instead it just returns the static value stored in the return_value attribute.
# Obviously you can also store a callable such as a function or an object,and the method will return it, but it will not run it.
m1.some_attribute.return_value = print_answer
m1.some_attribute()
# As you can see calling some_attribute() just returns the value stored in return_value, that is the function itself.
# To return values that come from a function we have to use a slightly more complex attribute of mock objects called side_effect.
Basic concepts unittest.mock – side_effect 1
# The side_effect parameter of mock objects is a very powerful tool. It accepts 3 different flavours of objects, callables,
# iterables, and exceptions, and changes its behaviour accordingly. If you pass an exception the mock will raise it
m1.some_attribute.side_effect = ValueError('A custom value error')
try: gentle_intro_to_mock1.py
m1.some_attribute()
except BaseException as ex:
print(f"ValueError: {ex}")

# If you pass an iterable, such as for example a generator, or a plain list, tuple, or similar objects, the mock will
# yield the values of that iterable, i.e. return every value contained in the iterable on subsequent calls of the mock.
m1.some_attribute.side_effect = range(3)
print(m1.some_attribute())
print(m1.some_attribute())
print(m1.some_attribute())
# print(m1.some_attribute())
try:
print(m1.some_attribute())
except BaseException as ex:
print(f"StopIteration().value: {StopIteration().value}")
# As promised, the mock just returns every object found in the iterable (in this case a range object) once at a time
# until the generator is exhausted. According to the iterator protocol once every item has been returned
# the object raises the StopIteration exception, which means that you can correctly use it in a loop.
Basic concepts unittest.mock – side_effect 2
# The last and perhaps most used case is that of passing a callable to side_effect, which shamelessly executes it with
# its own same parameters. This is very powerful, especially if you stop thinking about "functions" and start considering
# "callables". Indeed, side_effect also accepts a class and calls it, that is it can instantiate objects. Let us consider
# a simple example with a function without arguments
class Number(object):
m1.some_attribute.side_effect = print_answer
def __init__(self, value):
m1.some_attribute()
self._value = value
def print_value(self):
# A slightly more complex example: a function with arguments
print("Value:", self._value)
m1.some_attribute.side_effect = print_number
m1.some_attribute.side_effect(5)
def print_answer():
# And finally an example with a class print("42")
m1.some_attribute.side_effect = Number def print_number(num):
n1 = m1.some_attribute.side_effect(26) print(f"Number: {num}")
n1.print_value()
gentle_intro_to_mock1.py
# All the default mock methods / attributes
# ['assert_any_call', 'assert_called', 'assert_called_once', 'assert_called_once_with', 'assert_called_with',
# 'assert_has_calls', 'assert_not_called', 'attach_mock', 'call_args', 'call_args_list', 'call_count', 'called',
# 'configure_mock', 'method_calls', 'mock_add_spec', 'mock_calls', 'reset_mock', 'return_value', 'side_effect']
# From: http://www.thedigitalcatonline.com/blog/2016/03/06/python-mocks-a-gentle-introduction-part-1/
Basic concepts unittest.mock - assert_called_with
# myobj.py
• According to Sandi Metz (programmer and author) we need class MyObj():

to test only 3 types of messages (calls) between objects def __init__(self, repo):
self._repo = repo

Incoming queries (assertion on result) repo.connect()
from unittest import mock

Incoming commands (assertion on import unittest def setup(self):
direct public side effects) import myobj self._repo.setup(cache=True)

Outgoing commands (expectation on
call and arguments) class TestMocking(unittest.TestCase):
def test_instantiation(self):

https://www.sandimetz.com/
external_obj = mock.Mock()
• What we usually are interested myobj.MyObj(external_obj)
in when dealing with an external external_obj.connect.assert_called_with() # empty connect
object is to know that a given def test_setup(self):
method has been called on it external_obj = mock.Mock()
obj = myobj.MyObj(external_obj)
• Python mocks provide the obj.setup() # empty setup call
assert_called_with() method external_obj.setup.assert_called_with(
to check if a method has cache=True, max_connections=256) # AssertionError
been called on it if __name__ == '__main__':
unittest.main() test_gentle_intro_to_mock1.py
unittest.mock Mock class
• We check that the Hello.bar() method is called correct with ’HELLO’
• camelCase setUp() and tearDown() can perform initialization and cleanup of the test
fixture since they are called before and after the test
import unittest
from unittest.mock import Mock class Hello(object):
class HelloTestTest(unittest.TestCase): def foo(self, msg):
def setUp(self): MSG = msg.upper()
self.hello = Hello()
self.bar(MSG)
def tearDown(self): # the mocked bar() method
pass
# def bar(self, MSG):
def test_foo(self): # print(MSG)
msg = 'hello'
expected_bar_arg = 'HELLO'
self.hello.bar = Mock() # we mock the bar() method
self.hello.foo(msg)
# we check that Hello.bar() was called with 'HELLO'
self.hello.bar.assert_called_once_with(expected_bar_arg)

if __name__ == '__main__': test_hellomock.py


unittest.main(argv=['first-arg-is-ignored'], exit=False)
unittest.mock MagicMock class
• MagicMock is a subclass of Mock with default implementations of most of the magic methods
• You can use MagicMock without having to configure the magic methods yourself
import unittest
# employee.py (no method with name method())
from unittest.mock import MagicMock
class Employee:
from employee import Employee
"""A sample Employee class"""
class TestMocking(unittest.TestCase):
raise_amt = 1.05
def test_mock1(self):
thing = Employee('Hans', 'Jones', 50)
def __init__(self, first, last, pay):
# set return value
self.first = first
thing.method = MagicMock(return_value = 3)
self.last = last
# call the mocked method
self.pay = pay
ret = thing.method(3, 4, 5, key='value')
print(f'ret: {ret}')
# check if we called correct
thing.method.assert_called_with(3, 4, 5, key='value')
test_mock.py
if __name__ == '__main__':
unittest.main(argv=['first-arg-is-ignored'], exit=False)
The patchers
unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

The patch decorators are used for patching objects only within the scope of the function they decorate. They automatically
handle the unpatching for you, even if exceptions are raised. All of these functions can also be used in with statements or as class
decorators.
• patch() acts as a function decorator, class decorator or a context manager. Inside the body of the function or with statement, the
target is patched with a new object. When the function/with statement exits the patch is undone.
• If new is omitted, then the target is replaced with a MagicMock. If patch() is used as a decorator and new is omitted, the created
mock is passed in as an extra argument to the decorated function. If patch() is used as a context manager the created mock is
returned by the context manager.
• target should be a string in the form 'package.module.ClassName'. The target is imported and the specified object replaced with
the new object, so the target must be importable from the environment you are calling patch() from. The target is imported when
the decorated function is executed, not at decoration time.
• The spec and spec_set keyword arguments are passed to the MagicMock if patch is creating one for you.
• In addition you can pass spec=True or spec_set=True, which causes patch to pass in the object being mocked as the
spec/spec_set object.
• new_callable allows you to specify a different class, or callable object, that will be called to create the new object. By default
MagicMock is used.
• A more powerful form of spec is autospec. If you set autospec=True then the mock will be created with a spec from the object
being replaced. All attributes of the mock will also have the spec of the corresponding attribute of the object being replaced.
Methods and functions being mocked will have their arguments checked and will raise a TypeError if they are called with the
wrong signature. For mocks replacing a class, their return value (the ‘instance’) will have the same spec as the class. See the
create_autospec() function and Autospeccing.

Instead of autospec=True you can pass autospec=some_object to use an arbitrary object as the spec instead of the one
being replaced.
https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch
patch.object, patch.dict and patch.multiple
patch.object(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

• Patch the named member (attribute) on an object (target) with a mock object.
• patch.object() can be used as a decorator, class decorator or a context manager. Arguments new, spec, create,
spec_set, autospec and new_callable have the same meaning as for patch(). Like patch(), patch.object() takes
arbitrary keyword arguments for configuring the mock object it creates.
• When used as a class decorator patch.object() honours patch.TEST_PREFIX for choosing which methods to wrap.

patch.dict(in_dict, values=(), clear=False, **kwargs)

• Patch a dictionary, or dictionary like object, and restore the dictionary to its original state after the test.
• in_dict can be a dictionary or a mapping like container. If it is a mapping then it must at least support getting, setting
and deleting items plus iterating over keys.
• in_dict can also be a string specifying the name of the dictionary, which will then be fetched by importing it.
• values can be a dictionary of values to set in the dictionary. values can also be an iterable of (key, value) pairs.

patch.multiple(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

• Perform multiple patches in a single call. It takes the object to be patched (either as an object or a string to fetch the
object by importing) and keyword arguments for the patches
• Use DEFAULT as the value if you want patch.multiple() to create mocks for you. In this case the created mocks are
passed into a decorated function by keyword, and a dictionary is returned when patch.multiple() is used as a context
manager
https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch
unittest.mock patch() function
• patch() is the main mocking mechanism for the unittest.mock library
• patch() works by (temporarily) changing the object that a name points to with another one
• The basic principle is that you patch where an object is looked up, which is not necessarily the
same place as where it is defined
class TestMocking(unittest.TestCase):
# mocking MyMockedClass
import unittest
def test_mock2(self): from unittest.mock import patch
# Patching a class replaces the class with a MagicMock instance
with patch('__main__.MyMockedClass') as MockClass: class MyMockedClass:
# return_value of the mock will be used def method(self):
instance = MockClass.return_value pass
instance.method.return_value = 'foo' # set method() return value
# assert MyMockedClass() is instance of MyMockedClass
self.assertIsInstance(instance, type(MyMockedClass()))
self.assertEqual(MyMockedClass().method(), 'foo')
self.assertEqual(instance.method(), 'foo')

if __name__ == '__main__':
test_mock.py
unittest.main(argv=['first-arg-is-ignored'], exit=False)
unittest.mock Quick Guide – patch()
from unittest.mock import Mock # import the Mock class class ClassName1():
from unittest.mock import MagicMock # import the MagicMock class
def __init__(self):
from unittest.mock import patch
import myobj Pass
quick_guide_mock2.py
class ProductionClass():
def method(self, a, b, c):
myobj.py class ClassName2():
def __init__(self):
Pass
# The patch() decorator / context manager makes it easy to mock classes or objects in a module under test. pass
# The object you specify will be replaced with a mock (or other object) during the test and restored when the test ends
@patch('myobj.ClassName2')
@patch('myobj.ClassName1') # bottom-up passing of values # The patch.dict()
def test(MockClass1, MockClass2): # patched classes are input foo = {'key': 'value'}
myobj.ClassName1() # call patched class original = foo.copy()
myobj.ClassName2() with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
assert MockClass1 is myobj.ClassName1 # patched class is genuine assert foo == {'newkey': 'newvalue'} # foo has new values
assert MockClass2 is myobj.ClassName2 assert foo == original # foo has original values
assert MockClass1.called == True # patched class have been called
assert MockClass2.called == True
MockClass1.return_value = 'No AssertionError!' # output
return MockClass1.return_value method_cs1
print(f'\ntest(): {test()}') # printed values from test() comes from patched objects test(): No AssertionError!
mock_method.assert_called_once_with(1, 2, 3):
# The patch.object()
with patch.object(ProductionClass, 'method', return_value=None) as mock_method: None
thing = ProductionClass()
thing.method(1, 2, 3)
print(mock_method.assert_called_once_with(1, 2, 3)) # patched class have been called once with (1, 2, 3) and retuns None (no AssertionError)
unittest.mock Quick Guide – autospec
# Mock supports the mocking of Python magic methods. The easiest way of using magic methods is with the MagicMock class. It allows you to do things like
mock = MagicMock()
mock.__str__.return_value = 'foobarbaz' # return str(self)
print(str(mock)) # 'foobarbaz'
print(mock.__str__.assert_called_with()) # None
# Mock allows you to assign functions (or other Mock instances) to magic methods and they will be called appropriately.
# The MagicMock class is just a Mock variant that has all of the magic methods pre-created for you (well, all the useful ones anyway).
# The following is an example of using magic methods with the ordinary Mock class:
mock = Mock()
mock.__str__ = Mock(return_value='wheeeeee')
print(str(mock)) #'wheeeeee' quick_guide_mock2.py
# note the () difference
mock.return_value = 'whoooooo'
myobj.py
print(str(mock())) #'whoooooo'
# For ensuring that the mock objects in your tests have the same api as the objects they are replacing, you can use auto-speccing.
# Auto-speccing can be done through the autospec argument to patch, or the create_autospec() function. Auto-speccing creates mock objects that have the
# same attributes and methods as the objects they are replacing, and any functions and methods
# (including constructors) have the same call signature as the real object.
# This ensures that your mocks will fail in the same way as your production code if they are used incorrectly:
from unittest.mock import create_autospec
# output
def function(a, b, c):
pass str(mock): foobarbaz
mock_function = create_autospec(function, return_value='fishy') mock.__str__.assert_called_with(): None
print(mock_function(1, 2, 3)) # 'fishy' str(mock): wheeeeee
print(mock_function.assert_called_once_with(1, 2, 3))
str(mock()): whoooooo
#mock_function('wrong arguments') # TypeError: missing a required argument: 'b'
# create_autospec() can also be used on classes, where it copies the signature of the mock_function(1, 2, 3): fishy
# __init__ method, and on callable objects where it copies the signature of the __call__ method. mock_function.assert_called_once_with(1, 2, 3): None
patch() and new_callable
• patch() away sys.std - we replace an object (sys.stdout) with an io.StringIO instance with the @patch
decorator
• patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None,
new_callable=None, **kwargs)
from io import StringIO
import unittest
from unittest.mock import patch
class MyMockedClass:
def foo_print(self): test_mock.py
print('Something that is not going to be printed to sys.stdout')

class TestMocking(unittest.TestCase):
@patch('sys.stdout', new_callable=StringIO)
def test_mock3(self, mock_stdout):
thing = MyMockedClass()
thing.foo_print() # print to sys.stdout is redirected to mock_stdout
# assert mock_stdout.getvalue() == 'Something\n' # sys.stdout.getvalue()
self.assertEqual(mock_stdout.getvalue(), 'Something that is not going to be printed to sys.stdout\n')

if __name__ == '__main__':
unittest.main(argv=['first-arg-is-ignored'], exit=False)
Basic concepts unittest.mock – patch 1
import unittest # fileinfo.py
from fileinfo import FileInfo import os
from unittest.mock import patch
from logger import Logger class FileInfo:
def __init__(self, path):
class TestMocking(unittest.TestCase): self.original_path = path
self.filename = os.path.basename(path)
@classmethod
def setUpClass(cls): def get_info(self):
cls.filename = 'somefile.ext' return self.filename, self.original_path,

def test_init_filename(self): os.path.abspath(self.filename)


fi = FileInfo(self.filename) def get_info_size(self):
self.assertEqual(fi.filename, self.filename) return self.filename, self.original_path,
def test_init_path(self): os.path.abspath(self.filename),
relative_path = f'../{self.filename}' os.path.getsize(self.filename)
fi = FileInfo(relative_path)
self.assertEqual(fi.filename, self.filename)

def test_get_info1(self): test_gentle_intro_to_mock2.py


original_path = f'../{self.filename}'
with patch('os.path.abspath') as abspath_mock: # patching os.path.abspath with patch()
test_abspath = 'some/abs/path'
abspath_mock.return_value = test_abspath # mocking os.path.abspath
fi = FileInfo(original_path)
retval = fi.get_info()
self.assertEqual(retval, (self.filename, original_path, test_abspath))
Basic concepts unittest.mock – patch 2
@patch('os.path.abspath') # The patching decorator
def test_get_info2(self, abspath_mock): test_gentle_intro_to_mock2.py
original_path = f'../{self.filename}'
test_abspath = 'some/abs/path'
abspath_mock.return_value = test_abspath
fi = FileInfo(original_path)
self.assertEqual(fi.get_info(), (self.filename, original_path, test_abspath))
@patch('os.path.getsize') # Multiple patches
@patch('os.path.abspath') # bottom-up passing of values
def test_get_info3(self, abspath_mock, getsize_mock):
original_path = f'../{self.filename}'
test_abspath = 'some/abs/path'
abspath_mock.return_value = test_abspath
test_size = 1234
getsize_mock.return_value = test_size
fi = FileInfo(original_path)
self.assertEqual(fi.get_info_size(), (self.filename, original_path, test_abspath, test_size))
def test_get_info4(self):
original_path = f'../{self.filename}' # We can write the above test using two with statements as well
with patch('os.path.abspath') as abspath_mock:
test_abspath = 'some/abs/path'
abspath_mock.return_value = test_abspath
with patch('os.path.getsize') as getsize_mock:
test_size = 1234
getsize_mock.return_value = test_size
fi = FileInfo(original_path)
self.assertEqual(fi.get_info_size(), (self.filename, original_path, test_abspath, test_size))
Basic concepts unittest.mock – patch 3
# Patching immutable objects == no change after creation # logger.py
import datetime
def test_init_logger(self): class Logger:
lg = Logger() def __init__(self):
self.assertEqual(lg.messages, []) self.messages = []

# @patch('datetime.datetime.now')
# def test_log1(self, mock_now): def log(self, message):
# test_now = 123 self.messages.append((datetime.datetime.now(),
# test_message = "A test message"
message))
# mock_now.return_value = test_now
# lg = Logger()
# lg.log(test_message)
# self.assertEqual(lg.messages, [(test_now, test_message)]) test_gentle_intro_to_mock2.py
@patch('logger.datetime.datetime')
def test_log2(self, mock_datetime): test_get_info1 (__main__.TestMocking) ... ok
test_now = 123 test_get_info2 (__main__.TestMocking) ... ok
test_message = "A test message"
mock_datetime.now.return_value = test_now test_get_info3 (__main__.TestMocking) ... ok
lg = Logger() test_get_info4 (__main__.TestMocking) ... ok
lg.log(test_message)
self.assertEqual(lg.messages, [(test_now, test_message)]) test_init_filename (__main__.TestMocking) ... ok
test_init_logger (__main__.TestMocking) ... ok
if __name__ == '__main__': test_init_path (__main__.TestMocking) ... ok
unittest.main(argv=['first-arg-is-ignored'], exit=False)
test_log2 (__main__.TestMocking) ... ok
unittest.mock patch() a web request
• To get setup and teardown code that runs in a class before tests are
run we can have decorated class methods in camelCase
• To mock a web site request with patch() and @classmethod
check URL call and returned result def setUpClass(cls):
print('setupClass')
from unittest.mock import patch
class TestEmployee(unittest.TestCase):
def test_monthly_schedule(self): @classmethod
with patch('employee.requests.get') as mocked_get: def tearDownClass(cls):
# set return values print('teardownClass')
mocked_get.return_value.ok = True
mocked_get.return_value.text = 'Success'
# perform a mock call with emp_1
schedule = self.emp_1.monthly_schedule('May') test_employee.py
mocked_get.assert_called_with('http://company.com/Schafer/May')
self.assertEqual(schedule, 'Success')
import unittest Python unittest.mock example
from unittest.mock import patch
import requests
----------- OUTPUT -----------

PS C:\python_unittesting> python .\test_employee.py -v


from employee import Employee
class Employee: setupClass
class TestEmployee(unittest.TestCase):
"""A sample Employee class""" test_apply_raise (__main__.TestEmployee) ...
@classmethod
raise_amt = 1.05 setUp
def setUpClass(cls):
test_apply_raise
print('setupClass')
def __init__(self, first, last, pay): tearDown
@classmethod ok
self.first = first
def tearDownClass(cls): test_email (__main__.TestEmployee) ...
self.last = last
print('teardownClass') setUp
self.pay = pay
def setUp(self): test_email

print('\nsetUp') tearDown
@property
ok
self.emp_1 = Employee('Corey', 'Schafer', 50000)
def email(self):
test_fullname (__main__.TestEmployee) ...
self.emp_2 = Employee('Sue', 'Smith', 60000)
return 'f{self.first}.{self.last}@email.com' setUp
def tearDown(self):
test_fullname
print('tearDown') @property
tearDown
#… additional code here removed for readability def fullname(self): ok
def test_monthly_schedule(self):
return 'f{self.first} {self.last}' test_monthly_schedule (__main__.TestEmployee) ...
with patch('employee.requests.get') as mocked_get: setUp
mocked_get.return_value.ok = True def apply_raise(self): tearDown
mocked_get.return_value.text = 'Success' self.pay = int(self.pay * self.raise_amt) ok

schedule = self.emp_1.monthly_schedule('May') teardownClass

mocked_get.assert_called_with('http://company.com/Schafer/May') ------------------------------------------------------
#… additional code here removed for readability
self.assertEqual(schedule, 'Success') Ran 4 tests in 0.007s
def monthly_schedule(self, month):
mocked_get.return_value.ok = False
response = requests.get(f'http://company.com/{self.last}/{month}')
schedule = self.emp_2.monthly_schedule('June')
if response.ok:
mocked_get.assert_called_with('http://company.com/Smith/June')
return response.text
self.assertEqual(schedule, 'Bad Response!')
else: test_employee.py
if __name__ == '__main__':
unittest.main(argv=['first-arg-is-ignored'], exit=False)
return 'Bad Response!' and employee.py
IoC, DIP, DI and IoC containers
• IoC – Loose coupling between classes
• DIP – High-level modules should not depend
on low level modules. Both both should
depend on abstractions. Abstractions should
not depend on details. Details should depend
upon abstractions.
• DI – A design pattern which implements the
IoC principle to invert the creation of
dependent objects
• IoC container – A framework used to manage
automatic dependency injection
https://www.tutorialsteacher.com/ioc/
Python and Dependency Injection 1
• Dependency Injection is a pattern that decreases coupling and increases
cohesion (forming a united whole) via the Dependency Inversion Principle

The D in SOLID: https://en.wikipedia.org/wiki/Dependency_inversion_principle
• The DI design pattern implements the Inversion of Control (IoC) principle to
resolve dependencies: https://en.wikipedia.org/wiki/Dependency_injection

If object A (a client) depends on object B (a service), object A must not create or
import object B directly - Instead object A must provide a way to inject object B

The responsibilities of objects creation and dependency injection are usually
delegated to some external code - the dependency injector
• There are several ways to inject a service (B) into a client (A)

by passing it (B) as an __init__ argument (constructor / initialize injection)

by setting it (B) as an attribute’s value (attribute injection)
di_python.py

by passing it (B) as a method’s argument (method injection) di_python_factory.py
vehicle.py
Python and Dependency Injection 2
• The dependency injection pattern has a few strict rules that should be
followed

The client (A) delegates to the dependency injector the responsibility of injecting its
dependencies - the services (B)

The client should not know how to create the service, it should only know the interface of the
service

The service should not know about that it is used by the client

The dependency injector knows how to create the client and the service

It also knows that the client depends on the service, and knows how to inject the service into the
client

The client and service should know nothing about the dependency injector
• Python Dependency Dependency Injector

https://github.com/ets-labs/python-dependency-injector
di_python.py
• Python IoC Container di_python_factory.py

https://github.com/eyaldror/di_container vehicle.py
Dependency Injection (Dis)Advantages
• Advantages

Control of application structure

Decreased coupling of application components

Increased code reusability

Increased testability

Increased maintainability

Reconfiguration of a system without rebuilding
• Disadvantages

One need to explicitly specify the dependencies

Some extra work in the beginning
Recommended viewing and reading
• Python Mocks: a gentle introduction - Part 1 and 2

http://www.thedigitalcatonline.com/blog/2016/03/06/python-mocks-a-gentle-introduction-part-1/
• Python 3.x unittest.mock documentation

https://docs.python.org/3/library/unittest.mock.html
• What the mock? - A cheatsheet for mocking in Python

https://medium.com/@yeraydiazdiaz/what-the-mock-cheatsheet-mocking-in-python-
6a71db997832

https://github.com/yeraydiazdiaz/wtmock
• Test Driven Development (TDD) with Python – Mock Objects

https://rubikscode.net/2019/03/11/test-driven-development-tdd-with-python-mock-objects/
• Python Tutorial: Unit Testing Your Code with the unittest Module

https://www.youtube.com/watch?v=6tNS--WetLI
• Demystifying the Patch Function

https://www.youtube.com/watch?v=ww1UsGZV8fQ

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy