I’m testing complex logic that require joining of central fact table with 10-20 smaller dimensional tables. I want to mock that 10-20 smaller tables.
How to patch methods return values in a for loop? See code below.
tables.py:
JavaScript
x
16
16
1
class BaseClass(object):
2
3
def load(path: str):
4
5
6
class SmallTable1(BaseClass):
7
8
9
def load():
10
super().load(self.path)
11
12
class SmallTable20(BaseClass):
13
14
def load():
15
super().load(self.path)
16
test_logic.py
JavaScript
1
20
20
1
# QUESTION - how to make it work
2
def test_not_work(datasets):
3
for i in range(1, 21):
4
table = 'SmallTable' + str(i)
5
with mock.patch('some_package.tables.{}.load'.format(table)) as load_mock:
6
load_mock.return_value = datasets[table]
7
do_joins() # here my mocks doesn't work
8
9
10
def test_works(datasets):
11
with mock.patch('some_package.tables.SmallTable1.load'.format(i)) as load_mock_1:
12
load_mock_1.return_value = datasets[SmallTable1]
13
14
with mock.patch('some_package.tables.SmallTable2.load'.format(i)) as load_mock_2:
15
load_mock_2.return_value = datasets[SmallTable2]
16
17
# repeat the same 8-18 times more ..
18
19
do_joins() # here my mocks do work, but with cumbersome code and huge right offset
20
P.S. alternatively I can try to mock the BaseClass.load
, but then I don’t know how to return the different data set for different table (class).
Advertisement
Answer
Under the assumption that do_join
shall be called outside the loop, with all tables patched, you could write a fixture that uses contextlib.ExitStack to setup all mocks:
JavaScript
1
25
25
1
from contextlib import ExitStack
2
from unittest import mock
3
import pytest
4
5
from some_package import do_join
6
7
8
@pytest.fixture
9
def datasets():
10
11
12
@pytest.fixture
13
def mock_tables(datasets):
14
with ExitStack() as stack:
15
for i in range(1, 21):
16
table = 'SmallTable' + str(i)
17
load_mock = stack.enter_context(
18
mock.patch('some_package.tables.{}.load'.format(table)))
19
load_mock.return_value = datasets[table]
20
yield
21
22
23
def test_join(mock_tables):
24
do_join()
25
This means that all mocks are still active at yield time, and will only be removed after the test is finished.
If you have pytest-mock
installed, you can use the mocker
fixture instead:
JavaScript
1
8
1
@pytest.fixture
2
def mock_tables(datasets, mocker):
3
for i in range(1, 21):
4
table = 'SmallTable' + str(i)
5
load_mock = mocker.patch('some_package.tables.{}.load'.format(table))
6
load_mock.return_value = datasets[table]
7
yield
8