Python unittest 模拟配置没有扩散到测试方法

Python unittest mock configuration not proliferating to test method

我有一个复杂的 class,我想在测试中对其进行模拟。但是,我希望能够在我的模拟 class 上设置一个特定的属性,以便我的被测函数能够工作。下面是简单的、运行具有实际和预期输出的可用示例。

在我的示例中,为什么我的模拟配置没有扩散到 my_module.instantiate_class_do_stuff?很明显 MyClass 确实被嘲笑了,但我试图配置 MyClass.a 的嘲笑只是没有坚持。

目录内容 tmp:

tmp
├── __init__.py
├── my_module.py
└── test_my_module.py

请注意 __init__.py 为空。

文件内容 my_module.py:

class MyClass:
    def __init__(self):
        # Do expensive operations that will be mocked in testing.
        self.a = 7


def instantiate_class_do_stuff():
    """Create MyClass instance and do stuff with it"""
    instance = MyClass()
    print('Value of a in instantiate_class_call_method: {}'.format(instance.a))
    # Do stuff with instance...

文件内容 test_my_module.py:

import unittest
from unittest.mock import patch
from tmp import my_module


class MyTestCase(unittest.TestCase):
    def setUp(self):
        print('*' * 79)

    def test_create_class_call_method_1(self):
        """Try using the Mock's configure_mock method."""

        with patch('tmp.my_module.MyClass') as p:
            p.configure_mock(a=10)
            print('Value of a in test_create_class_call_method_1: {}'
                  .format(p.a))
            my_module.instantiate_class_do_stuff()

        self.assertTrue(True)

    def test_create_class_call_method_2(self):
        """Try passing in kwargs to patch, which should make it to
        configure_mock, according to the docs.
        """

        with patch('tmp.my_module.MyClass', a=10) as p:
            print('Value of a in test_create_class_call_method_2: {}'
                  .format(p.a))
            my_module.instantiate_class_do_stuff()

        self.assertTrue(True)

    def test_create_class_call_method_alternate_2(self):
        """Try using patch.object instead of plain patch."""

        with patch.object(my_module, 'MyClass', a=10) as p:
            print('Value of a in test_create_class_call_method_3: {}'
                  .format(p.a))
            my_module.instantiate_class_do_stuff()

        self.assertTrue(True)


if __name__ == '__main__':
    unittest.main()

来自 运行ning test_my_module.py 的实际输出:

*******************************************************************************
Value of a in test_create_class_call_method_1: 10
Value of a in instantiate_class_call_method: <MagicMock name='MyClass().a' id='140029598201104'>
*******************************************************************************
Value of a in test_create_class_call_method_2: 10
Value of a in instantiate_class_call_method: <MagicMock name='MyClass().a' id='140029598270096'>
*******************************************************************************
Value of a in test_create_class_call_method_3: 10
Value of a in instantiate_class_call_method: <MagicMock name='MyClass().a' id='140029598347088'>

运行ning test_my_module.py 的预期输出:

*******************************************************************************
Value of a in test_create_class_call_method_1: 10
Value of a in instantiate_class_call_method: 10
*******************************************************************************
Value of a in test_create_class_call_method_2: 10
Value of a in instantiate_class_call_method: 10
*******************************************************************************
Value of a in test_create_class_call_method_3: 10
Value of a in instantiate_class_call_method: 10

那么,当我的被测函数实际上是运行时,如何让我的属性配置生效?

嗯,这对我来说当然不是很直观,但我想通了。

我注意到在我的测试方法中,p 的值(MyClass 的模拟实例)有一个 __repr__<MagicMock name='MyClass' id='140054079807440'>.

然而,在被测函数内部,instantiate_class_do_stuffinstance 的值有一个 __repr__,如 <MagicMock name='MyClass()' id='140054079941392'>。区别在于 MyClass.

之后的 ()

所以,看来我没有模拟正确的东西 - 我想模拟 return 值 上的 a 属性MyClass

所以,这是一个有效的测试:

def test_creat_class_call_method(self):
    # Create a mock which will be returned by MyClass.
    m = MagicMock()
    m.a = 10

    with patch('tmp.my_module.MyClass', return_value=m) as p:
        my_module.instantiate_class_do_stuff()

并且 my_module.instantiate_class_do_stuff 中的 print 语句打印以下内容:

Value of a in instantiate_class_do_stuff: 10

成功!