模拟被测函数中使用的同一类的多个实例

Nem*_*lis 1 python mocking python-unittest

我有一个函数,它使用同一个类的 2 个实例,我想使用 python 模拟来测试该函数。但我很头疼我怎么能嘲笑这个。我已经使用我的朋友 Google 和 Stackoverflow 来寻找解决方案,但到目前为止还没有找到任何解决方案。

作为简单的模拟示例,它显示了我要测试的原理:

class MyClass(object): # this class is inside a module I'm not allowed to change.
    def __init__(self, name, value):
        self._name  = name
        self._value = value

    def get_value(self):
        return self._value
Run Code Online (Sandbox Code Playgroud)

然后是被测模块(再次是一个模拟实现来展示这个想法):

import my_class_module

def my_function():
    a = my_class_module.MyClass('a', 3)
    b = my_class_module.MyClass('b', 4)

    return a.get_value() + b.get_value()
Run Code Online (Sandbox Code Playgroud)

我想到的 my_function 的单元测试的全局想法如下(但这个想法目前当然行不通):

mock.patch('module.my_class_module.MyClass')
def test_my_function(self, mock_my_class):
    def _my_get_value(self): # ideally I would like to use get_value.return_value per instance, but I don't know how.
        if self._name == 'a':
            return 4 # Different values to make sure that we can verify that the mock is called
        else:
            return 5

    # Next line is the one I'm wondering about since mock_myh_class.return_value should differ for each instance.
    # But again this next line is already a work-around idea, since I don't know how to set the return-value of get_value for each instance.
    mock_my_class.return_value.get_value.side_effect = my_get_value 

    exp_result = 9

    result = module.my_function()

    self.assertEqual(result, exp_result)
Run Code Online (Sandbox Code Playgroud)

为了使这项工作顺利进行,我缺少什么?又名:如何模拟同一类的 2 个实例?

sli*_*wp2 5

您可以使用mock.side_effect创建不同的模拟实例MyClass

例如

myclass.py:

class MyClass(object):
    def __init__(self, name, value):
        self._name = name
        self._value = value

    def get_value(self):
        return self._value
Run Code Online (Sandbox Code Playgroud)

myfunction.py:

import myclass


def my_function():
    a = myclass.MyClass('a', 3)
    b = myclass.MyClass('b', 4)
    print('a:\n', a)
    print('b:\n', b)
    print('a and b are different instances of MyClass:', a != b)
    return a.get_value() + b.get_value()
Run Code Online (Sandbox Code Playgroud)

test_myfunction.py:

import unittest
from unittest import mock
import myfunction


class TestMyFunctionModule(unittest.TestCase):
    @mock.patch('myclass.MyClass')
    def test_my_function(self, mock_my_class):
        myclassInstanceMocks = [mock.Mock(name='a'), mock.Mock(name='b')]
        myclassInstanceMocks[0].get_value.return_value = 4
        myclassInstanceMocks[1].get_value.return_value = 5
        mock_my_class.side_effect = myclassInstanceMocks
        exp_result = 9
        result = myfunction.my_function()
        self.assertEqual(result, exp_result)


if __name__ == '__main__':
    unittest.main()
Run Code Online (Sandbox Code Playgroud)

带有覆盖率报告的单元测试结果:

a:
 <Mock name='a' id='4572131536'>
b:
 <Mock name='b' id='4572149072'>
a and b are different instances of MyClass: True
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK
Name                                            Stmts   Miss  Cover   Missing
-----------------------------------------------------------------------------
src/stackoverflow/64170793/myclass.py               6      3    50%   3-4, 7
src/stackoverflow/64170793/myfunction.py            8      0   100%
src/stackoverflow/64170793/test_myfunction.py      14      0   100%
-----------------------------------------------------------------------------
TOTAL                                              28      3    89%
Run Code Online (Sandbox Code Playgroud)