简体   繁体   English

Python unittest模拟配置不会扩散到测试方法

[英]Python unittest mock configuration not proliferating to test method

I have a complex class which I would like to mock in testing. 我有一个想在测试中模拟的复杂类。 However, I want to be able to set a particular attribute on my mocked class so that my function under test will work. 但是,我希望能够在我的模拟类上设置一个特定的属性,以便我的被测函数能够正常工作。 Simple, runnable example with actual and expected output is below. 下面是具有实际和预期输出的简单,可运行的示例。

In my example, why doesn't my mock configuration proliferate to my_module.instantiate_class_do_stuff ? 在我的示例中,为什么我的模拟配置不会激增到my_module.instantiate_class_do_stuff It's clear that MyClass is indeed being mocked, but my attempt to configure the mocking of MyClass.a just isn't sticking. 显然, MyClass确实在被嘲笑,但是我配置MyClass.a的模仿的尝试并没有坚持。

Contents of directory tmp : 目录tmp内容:

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

Note that __init__.py is empty. 请注意__init__.py为空。

Contents of file my_module.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...

Contents of file test_my_module.py : 文件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()

Actual output from running test_my_module.py : 运行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'>

Expected output from running test_my_module.py : 来自运行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

So, how can I make my attribute configuration be effective when my function under test is actually run? 那么,当被测函数实际运行时,如何使属性配置有效?

Similar (but different) question I posted but didn't get a particularly satisfactory answer 我发布了类似(但不同)的问题,但没有得到特别令人满意的答案

Well, this certainly wasn't very intuitive to me, but I figured it out. 好吧,这对我来说当然不是很直观,但是我发现了。

I noticed that in my testing methods, the value of p (the mocked instance of MyClass ) has a __repr__ like <MagicMock name='MyClass' id='140054079807440'> . 我注意到在我的测试方法中, p的值( MyClass__repr__实例)具有__repr__<MagicMock name='MyClass' id='140054079807440'>

However, inside the function under test, instantiate_class_do_stuff , the value of instance has a __repr__ like <MagicMock name='MyClass()' id='140054079941392'> . 然而,在测试中,在函数内部instantiate_class_do_stuff ,的值instance具有__repr__<MagicMock name='MyClass()' id='140054079941392'> The difference is in the () after MyClass . 区别在于MyClass之后的()

So, it would appear that I wasn't mocking the correct thing - I want to mock the a attribute on the return value of MyClass . 因此,看来我不是在模拟正确的东西-我想在MyClass返回值上模拟a属性。

So, here's what a working test looks like: 因此,工作测试如下所示:

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

And the print statement in my_module.instantiate_class_do_stuff prints the following: 并且my_module.instantiate_class_do_stuffprint语句打印以下内容:

Value of a in instantiate_class_do_stuff: 10

Success! 成功!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM