-2

I read a lot of about unittest.mock but I am unable to transfer this to my own scenario. So I created a minimal working example here.

I simply replace a function before a test and set it back to the original one at the tests end - without using unittest.mock. The question is how can I replace the function or modify the return-value using unittest.mock.

Side-queston is if there is a way to reset the mock/patch implicit at the end of the TestCases. Currently it is done explicite in tearDownClass(). But sometimes I forget things like that.

#!/usr/bin/env python3

class MyData:
    me = None

    def __init__(self):
        MyData.me = self
        self.data = self._get_default_data()

    def _get_default_data(self):
        return "real default data"

if __name__ == '__main__':
    MyData()

    print(MyData.me.data)

This is the test

import unittest

from mockplay import MyData

def _test_data(self):
    return "simulated test data"

class MyTest_Sim(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # remember the original method
        cls.org_method = MyData._get_default_data
        # mock the method
        MyData._get_default_data = _test_data

        MyData()

    @classmethod
    def tearDownClass(cls):
        # rest the mock/patch to the origiinal
        MyData._get_default_data = cls.org_method

    def test_data(self):
        self.assertEqual(MyData.me.data,
                         "simulated test data")

Background information:

The real application reads user-related content (e.g. emails) from a JSON file. On the first start, directly after installation, there is no such JSON file. So the application uses an "in-built" default JSON file. Each test starts with a "fresh" application - without a JSON file on the filesystem. So it uses _get_default_data(). To control which data is present in a test I need to mock/patch/replace this method because the default JSON file a) is not suited for all test cases and b) can change during the development of the application.

buhtz
  • 10,774
  • 18
  • 76
  • 149
  • Why do you need to mock anything to test this? The class you're testing has no collaborators, no test doubles should be required. Also why are you patching the class with the instance at all? Is it an attempt at the singleton pattern? – jonrsharpe Jan 10 '21 at 16:06
  • It is a [Minimal Working Example](https://stackoverflow.com/help/minimal-reproducible-example). – buhtz Jan 10 '21 at 19:46
  • What's the output? The example isn't much use if it's not representative of the situation you actually face, though. The answer here is: you obviously don't need to mock anything, so no it's not suited to unittest.mock, but whether that applies to the actual case is unclear without more context. You certainly shouldn't patch out parts of the thing you're supposed to be testing. – jonrsharpe Jan 10 '21 at 19:49
  • I removed the side question and added some background informations about the real application. – buhtz Jan 10 '21 at 20:25
  • And the me = self thing? I'd suggest this could be a more architectural problem, if you need to create an instance from a file-or-default that should probably be a class method so testing the instance is cleaner. – jonrsharpe Jan 10 '21 at 20:28
  • Please concentrate on the MWE. This is not about architecture of the real application. I tried to keep the MWE **minimal**. – buhtz Jan 10 '21 at 20:34
  • Then in doing so you may have failed to make it an **example**. The architecture of the system under test is absolutely relevant to the testing of it. Again: your class (as far as the example shows it) has no collaborators, so you shouldn't need any test doubles, and you should never be patching the thing you're supposedly testing (because that way lies *not actually testing it*). Maybe someone else will have a go, but without further context I'm done. – jonrsharpe Jan 10 '21 at 21:26
  • The question is not about testing. It is just about _replacing_ a function with another in the meaning of _mock_ing. – buhtz Jan 10 '21 at 22:02
  • 1
    Check for example the answer to [this question](https://stackoverflow.com/q/62502151/12480730) to check how to use `patch` in `setUp` / `tearDown`. – MrBean Bremen Jan 11 '21 at 08:03

1 Answers1

0

There is no need to replace the function. In my example only the return value of the function is in the interest.

The patch is created and explicite started in setupClass() and explicite stoped in tearDownClass().

import unittest
import unittest.mock as mock

from mockplay import MyData


class MyTest_Sim(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # mock the return value
        cls.patcher = mock.patch('mockplay.MyData._get_default_data',
                                 return_value='simulated test data')
        # start the patch
        cls.patcher.start()

        # initiate data
        MyData()

    @classmethod
    def tearDownClass(cls):
        # stop the patch
        cls.patcher.stop()

    def test_data(self):
        self.assertEqual(MyData.me.data,
                         "simulated test data")

Python 3.8 or newer

In this Python version the new method addClassCleanup() was introduced. So you do not need to take care to call patcher.stop() explicite at the end of all tests. The test class will do this for you if you do it like that:

@classmethod
def setUpClass(cls):
    # ...

    # start the patch
    cls.patcher.start()

    # stop after all tests
    cls.addClassCleanup(cls.patcher.stop)

    # ...
buhtz
  • 10,774
  • 18
  • 76
  • 149