so I'm writing unit tests and I'm having trouble with the setUp function. From what I've seen, it's supposed to just execute code before your function and thus I could put anything that's repetitive in there. However, this function doesn't seem to be applying the mocks I've created as patch decorators over the entire class. This is a small piece of what I want it to look like:
@patch('geomet_data_registry.layer.base.get_today_and_now', new=mocked_get_date) # noqa
@patch('geomet_data_registry.layer.base.load_plugin', new=mocked_load_plugin)
@patch('geomet_data_registry.layer.base.TILEINDEX_PROVIDER_DEF', new=mocked_tileindex) # noqa
@patch('geomet_data_registry.layer.base.STORE_PROVIDER_DEF', new=mocked_store)
class TestInitBase(unittest.TestCase):
def setUp(self):
""" Code that executes before every function. """
self.maxDiff = None
self.today_date = \
datetime.now(timezone.utc).isoformat().replace(' 00:00', 'Z')
mocked_get_date.return_value = self.today_date
self.base_layer = BaseLayer({'name': 'model_gem_global'})
def test_Init(self):
expected_values = {'items': [],...
base_layer_attr = self.base_layer.__dict__
self.assertDictEqual(expected_values, base_layer_attr, msg=None)
Here I mock the date received so it doesn't mess with my tests, I mock load_plugin, which when used returns a class instance of a certain plugin, I mock TileIndex which is an ES TileIndex and I mock the store, which is a redis store. If I use the code shown above, it doesn't work. When I instantiate the class BaseLayer
inside setUp
, none of my mocks are used and I get:
- 'receive_datetime': '2021-11-10T12:56:07.371067Z',
? ^^^
'receive_datetime': '2021-11-10T12:56:07.371131Z',
? ^^^
- 'store': <MagicMock name='mock()' id='140158154534472'>,
- 'tileindex': <MagicMock name='mock()' id='140158154534472'>,
'store': <BaseStore> Redis,
'tileindex': <ElasticsearchTileIndex> http://localhost:9200,
However, before you tell me maybe my paths are wrong for the mocks or something like that, I can assure you that everything works fine, because the code works if I keep everything the same, except I repeat the class instantiation in every test function. Moreover, it will work if I keep this the same, but I name the setUp
for example mySetUp
and call it at the beginning of the function.
Everything works this way and I've already made all my tests without using the setUp
at all because I remember thinking to myself "this thing is broken I'm not risking using this function in my tests".
Thanks!
CodePudding user response:
The problem is that the mock.patch
decorator is applied to each test function, and during setUp
the patching has not been done yet.
To use the same mocks for all tests, you have to start/stop mocking in setUp
/tearDown
instead. This could look something like:
class TestInitBase(unittest.TestCase):
def setUp(self):
self.data_patcher = patch('geomet_data_registry.layer.base.get_today_and_now',
new=mocked_get_date)
self.data_patcher.start()
self.plugin_patcher = patch('geomet_data_registry.layer.base.load_plugin',
new=mocked_load_plugin)
self.plugin_patcher.start()
...
self.today_date = \
datetime.now(timezone.utc).isoformat().replace(' 00:00', 'Z')
mocked_get_date.return_value = self.today_date
self.base_layer = BaseLayer({'name': 'model_gem_global'})
def tearDown(self):
self.data_patcher.stop()
self.plugin_patcher.stop()
...
Admittedly this is not as nice as using the decorators. You can still use the decorators for the functions that don't have to already be patched in setUp
, if there are any.
As a side note: with pytest
this would be a bit less messy, as you can have the setup and teardown part in the same fixture.