Quite often I run into the problem where I want use a setUp method in my unittest but in order to run the code in my setUp method I need to mock something. For example, I need to mock a Storage call using VIKTOR’s @mock_Storage decorator. Using @mock_Storage on the setUp method works fine as long as there is only one test in the test class (or each test is run individually). However, when adding more tests they will start to fail since the amount of mocked responses being asked by the tests are bigger than the amount of responses mocked. Let me explain with an example.
In a test class, see below, I need to create 4 instances of a GEF class in order to run my tests (cpt_obj_1, cpt_obj_2, cpt_obj_3 and borehole_obj_1). I create these instances in my setUp method to avoid having do so in each test (which would add a lot of extra copy/pasted code). However, in the GEF.from_gef_entity() call, VIKTOR’s Storage().get() is called which raises an error when testing. Therefore, I have used @mock_Storage() with 4 return values for get() since creating 4 objects results in 4 calls to Storage().get(). The values themselves do not matter since I am not testing what is stored in the storage so some dummy data has been added.
The code above works fine when I run my tests one by one but if I try to run them in bulk they crash because the second test call is making the 5th call to the mocked Storage but there are only 4 return values mocked. This is similar to a problem I have posted on the forum before Mock_api with multiple tests - 🗪 Support (Q&A) - VIKTOR Community.
There are a few solutions to this problem:
- Instead of mocking 4 return values I could mock 4 * <number_of_methods> return values to make sure that for each call to Storage().get() a mocked return value is created. However, I have not found a way to dynamically let python determine how many methods a class has while being in the class. Because of this I would need to constantly update the number_of_methods myself as I create or deprecate tests. This I deemed to not be a good solution.
- Move the @mock_Storage() decorator to each test. This is in line with python’s convention and would solve the problem but I would prefer to not have to add it to each test since it creates extra lines of code and can easily create chaos when @mock_Storage() needs to be used in combinations with @parameterized.expand() and/or @patch.mock()
I would like to share with you my preferred solution I have found to the problem above.
By adding a _create_objects() function inside my setUp method I can apply the decorators to this function. Calling the function inside the setUp method causes the objects to be created for each test. Since the function is called from scratch every time there is no longer a need to mock more than 4 return values for Storage().get(). Lastly, by adding the attributes to the class above the setUp method python is aware of these attributes and autocomplete will work all throughout your test class.
I hope you find this code snippet interesting and/or helpful. Any suggestions on how to improve the code are welcome!