Mock_api with multiple tests

I am writing unittests for a class with multiple class methods. In order to call the constructor of the class I am testing I use a setUp function in my testclass. In this setUp function I need to mock the API using the @mock_api decorator. Right now I have 4 tests which all call the setUp function. The problem I am facing is that the first time the mock_api works but the times after that it no longer seems to work. My code is shown below.

The constructor of the EntityGenerator is as follows:

More precisely, for the first test I run, the self.gef_entities in the constructor are filled properly with a list of MockedEntities. Any test after that though results in an empty self.gef_entities. The problem only occurs when I run all tests at once, if I run each test individually they all pass succesfully.

Hi Rutger,

Mocking is a broader concept outside of VIKTOR, the viktor mocking extensions are based on unittest.mock. The way these work is that the first time the function that you are mocking is called, it will return the first mocked output instead of running the function. The second time it will return the second mocked output and so on.

This is to prevent that the tests accidentality run with different data than you might expect. If you want the function to be “faked” two times, but instead it s called three times, that that should raise a red flag. This is why you see the Nones after the first call.

By convention, people use the @mock decorator on the test itself, rather than at the setup of every test. This makes sure that the mocking happens correctly no matter the order you run the tests. If you need to mock more than one thing in your test, you can stack them as follows:

    @mock_SciaAnalysis(
        get_engineering_report=example_pdf),
        get_updated_esa_model=example_esa),
        get_xml_output_file=example_xml)
    )
    @mock_API(get_entity_file=example_file)
    def test_get_view_with_two_api_calls(self):
        result = Controller().get_map_view(self.params, self.entity_id)
        self.assertIsInstance(result, MapResult)

Mocking can be a bit confusing, I hope this helps you understand what is happening.

Regards,
Paulien

1 Like

Thanks Paulien,

That explains a lot, I have moved away from my setUp function and create an object in my test itself using the mocked API now. I do have a question regarding that though. In one of my tests I want to test two cases. Case one being where I overwrite the params of a child entity and case two being where I create a new child entity. This means I will have to run the same test twice with slightly different input. How can I do this with mock_API to make sure both times I can initiate an object to run my test with?

Kind regards,
Rutger

Hi Rutger,

I would probably just split it up in two separate tests. Then you will also know if the first test failed, or the second, or both.

This will also make sure you do not accidentality run the second part of the test with the data that was overwritten in the first part of the test. Or you are mocking more API functions than you expected. I found that example code helped me best understand testing, so it might look something like this:

import unittest
from viktor.testing import MockedEntity, mock_API
from app.my_entity_type.controller import MyEntityTypeController

test_entity1 = MockedEntity(entity_id=98)
test_entity2 = MockedEntity(entity_id=99, last_saved_params={...})

class TestMyEntityTypeController(unittest.TestCase):

    @mock_API(get_entity=[test_entity1])
    def test_set_params(self):
        returned_dict = MyEntityTypeController().set_params()
        self.assertDictEqual(returned_dict, {...})

    @mock_API(get_entity=[test_entity1], create_child_entity=[test_entity2])
    def test_create_child_entity(self):
        returned_entity = MyEntityTypeController().create_children()
        self.assertDictEqual(returned_entity.last_saved_params, {...})

    @mock_SciaAnalysis(
        get_engineering_report=example_pdf),
        get_updated_esa_model=example_esa),
        get_xml_output_file=example_xml)
    )
    @mock_API(get_entity_file=example_file)
    def test_get_view_with_scia_results(self):
        result = MyEntityTypeController().get_scia_plotlyview(self.params, self.entity_id)
        self.assertIsInstance(result, PlotlyResult)