Mocking in python
Mocking in python
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)
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 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.
• 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,
# @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 -----------
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
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