Make sure that to not use unittest.mock.Mock
but instead unittest.mock.MagicMock
for the reasons stated here. You can follow this documentation about Mocking Classes (all of this will use MagicMock
).
For your case, here are 3 options to mock the object returned by load_plugin()
. You can choose what best fits your needs.
mock_plugin_return_values
- Mocks via return_value
mock_plugin_side_effect
- Mocks via side_effect
mock_plugin_stub
- Mocks via stubbing the class
File tree
.
├── folder
│ ├── layer
│ │ └── base.py
│ ├── plugin.py
│ └── tileindex
│ └── base.py
└── tests
└── test_layer.py
folder/layer/base.py
from folder.plugin import load_plugin
class BaseLayer:
def __init__(self):
self.tileindex = load_plugin()
def somefunction(self):
a = self.tileindex.add("a")
print("add:", a)
key = self.tileindex.get_key("a")
print("get_key:", key)
r = self.tileindex.bulk_add([1, 2, 3])
print("bulk_add:", r)
status = r['identifier']
print("status:", status)
return a, key, r, status
folder/plugin.py
from folder.tileindex.base import SomePlugin
def load_plugin():
return SomePlugin()
folder/tileindex/base.py
class SomePlugin():
pass
test/test_layer.py
import pytest
from folder.layer.base import BaseLayer
# Note, this requires <pip install pytest-mock>
@pytest.fixture
def mock_plugin_return_values(mocker):
mock_cls = mocker.patch("folder.plugin.SomePlugin")
mock_obj = mock_cls.return_value
mock_obj.add.return_value = "Anything!"
mock_obj.get_key.return_value = "Something!"
mock_obj.bulk_add.return_value = {"identifier": "Nothing!"}
@pytest.fixture
def mock_plugin_side_effect(mocker):
mock_cls = mocker.patch("folder.plugin.SomePlugin")
mock_obj = mock_cls.return_value
mock_obj.add.side_effect = lambda arg: f"Adding {arg} here"
mock_obj.get_key.side_effect = lambda arg: f"Getting {arg} now"
mock_obj.bulk_add.side_effect = lambda arg: {"identifier": f"Adding the {len(arg)} elements"}
@pytest.fixture
def mock_plugin_stub(mocker):
# Option 1: Create a new class
# class SomePluginStub:
# Option 2: Inehrit from the actual class and just override the functions to mock
from folder.tileindex.base import SomePlugin
class SomePluginStub(SomePlugin):
def add(self, arg):
return f"Adding {arg} here"
def get_key(self, arg):
return f"Getting {arg} now"
def bulk_add(self, arg):
return {"identifier": f"Adding the {len(arg)} elements"}
mocker.patch("folder.plugin.SomePlugin", SomePluginStub)
def test_return_values(mock_plugin_return_values):
layer = BaseLayer()
result = layer.somefunction()
print(result)
assert result == ('Anything!', 'Something!', {'identifier': 'Nothing!'}, 'Nothing!')
def test_side_effect(mock_plugin_side_effect):
layer = BaseLayer()
result = layer.somefunction()
print(result)
assert result == ('Adding a here', 'Getting a now', {'identifier': 'Adding the 3 elements'}, 'Adding the 3 elements')
def test_stub(mock_plugin_stub):
layer = BaseLayer()
result = layer.somefunction()
print(result)
assert result == ('Adding a here', 'Getting a now', {'identifier': 'Adding the 3 elements'}, 'Adding the 3 elements')
Output
$ pytest -q -rP
... [100%]
=========================================== PASSES ============================================
_____________________________________ test_return_values ______________________________________
------------------------------------ Captured stdout call -------------------------------------
add: Anything!
get_key: Something!
bulk_add: {'identifier': 'Nothing!'}
status: Nothing!
('Anything!', 'Something!', {'identifier': 'Nothing!'}, 'Nothing!')
______________________________________ test_side_effect _______________________________________
------------------------------------ Captured stdout call -------------------------------------
add: Adding a here
get_key: Getting a now
bulk_add: {'identifier': 'Adding the 3 elements'}
status: Adding the 3 elements
('Adding a here', 'Getting a now', {'identifier': 'Adding the 3 elements'}, 'Adding the 3 elements')
__________________________________________ test_stub __________________________________________
------------------------------------ Captured stdout call -------------------------------------
add: Adding a here
get_key: Getting a now
bulk_add: {'identifier': 'Adding the 3 elements'}
status: Adding the 3 elements
('Adding a here', 'Getting a now', {'identifier': 'Adding the 3 elements'}, 'Adding the 3 elements')
3 passed in 0.06s