首页 > 解决方案 > Python unittest:通过模拟替换(或修补)函数

问题描述

我读了很多关于unittest.mock但我无法将其转移到我自己的场景中。所以我在这里创建了一个最小的工作示例。

我只是在测试之前替换一个函数,然后在测试结束时将其设置回原来的函数——而不使用unittest.mock. 问题是如何使用unittest.mock.

附带问题是是否有一种方法可以重置 TestCases 末尾隐含的模拟/补丁。目前它在tearDownClass(). 但有时我会忘记这样的事情。

#!/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)

这是测试

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")

背景资料

真正的应用程序从 JSON 文件中读取与用户相关的内容(例如电子邮件)。第一次启动,直接安装后,没有这个json文件。因此,应用程序使用“内置”默认 JSON 文件。每个测试都从一个“新”应用程序开始——文件系统上没有 JSON 文件。所以它使用_get_default_data(). 为了控制测试中存在哪些数据,我需要模拟/修补/替换此方法,因为默认 JSON 文件 a) 不适合所有测试用例,并且 b) 在应用程序开发过程中可能会发生变化。

标签: python-3.xpython-unittest.mock

解决方案


无需更换功能。在我的示例中,仅对函数的返回值感兴趣。

补丁被创建并且显式开始于setupClass()和显式停止于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 或更新版本

在这个 Python 版本中addClassCleanup(),引入了新方法。所以你不需要注意patcher.stop()在所有测试结束时调用explicite。如果您这样做,测试类将为您执行此操作:

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

    # start the patch
    cls.patcher.start()

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

    # ...

推荐阅读