当使用第二个patch参数与return_value时,为什么python mock.patch的工作方式不同?

时间:2019-03-16 21:45:17

标签: python python-3.x testing mocking python-unittest

我正在尝试模拟依赖方法的返回值,但是返回值在使用return_value和向mock.patch添加附加参数之间有所不同。请帮我找出原因。我尝试在线搜索,但找不到答案。

library / abc.py:

from tools.operating_system import os_name

class ABC(object):    
    def get_os_info(self):
        return os_name()

tools / operating_system.py:

import os

def os_name():
    return os.name

library / test_abc.py:

from unittest import TestCase, mock
from library.abc import ABC

class TestMain(TestCase):
    # This works because the name method returns `test`
    def test_mocking_os_name(self):
        with mock.patch('tools.operating_system.os.name', 'test'):
            abc = ABC()
            res = abc.get_os_info()
            self.assertEqual(res, 'test')

    # The test fails because the name method returns `<MagicMock name='name' id='4515046400'>`
    def test_mocking_os_name(self):
        with mock.patch('tools.operating_system.os.name') as mock_name:
            mock_name.return_value = 'test'
            abc = ABC()
            res = abc.get_os_info()
            self.assertEqual(res, 'test')

注意:此示例有些人为设计,因为我可以模拟os_name方法。这不是主要问题。我正在尝试学习python测试模拟,并且希望能够模拟os.name。谢谢!这是针对Python 3.7.2的,但是我对Python 2.7.15有同样的问题

1 个答案:

答案 0 :(得分:0)

请参阅mock.patch的文档:致电时

with mock.patch('tools.operating_system.os.name', 'test')

您正在这样做:

with mock.patch('tools.operating_system.os.name', NEW='test')

您将os.name替换为字符串test,以便按预期工作。

致电时

with mock.patch('tools.operating_system.os.name') as mock_name:

您正在这样做:

with mock.patch('tools.operating_system.os.name', NEW=DEFAULT) as mock_name:

您将NEW保留为NEW=DEFAULT,因此将os.name设置为MagicMock对象(如文档所述)。设置mock_name.return_value='test'不起作用,因为您没有在代码中的任何地方调用函数os.name(),当然也不应该这样做,因为os.name是字符串而不是函数。

就像您说的那样,在实际代码中,您希望修补os_name函数而不是os.name

def test_patching_os_name(self):
    with mock.patch('library.abc.os_name') as mock_os_name:
        mock_os_name.return_value = 'test'
        abc = ABC()
        res = abc.get_os_info()
        self.assertEqual(res, 'test')

此测试不依赖于os_name函数的内部实现。如果ABC类的正确行为取决于os_name总是返回os.name(这就是测试所断言的内容),那么该类也可以使用os.name本身,并且完全不依赖tools.operating_system

最后一点个人喜好:我更喜欢注入依赖而不是尽可能地打补丁,例如,参见文章"Every mock.patch() is a little smell"