Python unittest 模拟类和类方法 [英] Python unittest mock class and class method

查看:90
本文介绍了Python unittest 模拟类和类方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我觉得这可能相对简单,但我正在努力使其工作.我想模拟整个类,然后指定此类方法之一的返回值.

I feel like this may be relatively simple, but I'm pulling my hair out to get this working. I'd like to mock an entire class, and then specify the return value for one of this class's methods.

我已经看过这里,还有其他几个问题,当然还有 docs.我仍然无法让它发挥作用.请看我下面的简单例子.

I already looked here, at several other questions, and of course in the docs. I'm still unable to get this to work. Please see my simple example below.

目录tmp的内容:

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

my_module.py 的内容:

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

    def my_method(self):
        # For sake of simple example, always return 1.
        return 1


def create_class_call_method():
    """Create MyClass instance and call its my_method method, returning
    the result."""
    instance = MyClass()
    value = instance.my_method()
    return value

test_my_module.py 的内容:

import unittest
from unittest.mock import patch, Mock

from tmp import my_module


class MyClassTestCase(unittest.TestCase):

    def test_create_class_call_method(self):
        # Attempt to patch MyClass as well as specify a return_value for
        # the my_method method (spoiler: this doesn't work)
        with patch('tmp.my_module.MyClass',
                   my_method=Mock(return_value=2)):
            value = my_module.create_class_call_method()

        self.assertEqual(value, 2)


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

运行test_my_module.py的结果:

2 != <MagicMock name='MyClass().my_method()' id='140234477124048'>

Expected :<MagicMock name='MyClass().my_method()' id='140234477124048'>
Actual   :2

我尝试过的其他一些事情:

Some other things I've tried:

  • 而不是 ..., my_method=Mock(return_value=2))patch 语句中,像这样解压字典:**{'my_method.return_value': 2}
  • 嵌套 with patch 语句.外部语句就像 with patch('tmp.my_module.MyClass'): 一样简单,内部语句尝试像这样修补 my_method: with patch('tmp.my_module.MyClass.my_method, return_value=2)
  • 使用补丁装饰器代替上下文管理器
  • 将 patch 语句更改为 with patch('tmp.my_module.MyClass') as p: 然后在 with 语句中,尝试设置 p 像这样:p.evaluate = Mock(return_value=2)
  • Rather than ..., my_method=Mock(return_value=2)) in the patch statement, unpack a dictionary like so: **{'my_method.return_value': 2}
  • Nested with patch statements. Outer statement is simple like with patch('tmp.my_module.MyClass'):, inner statement attempts to patch my_method like so: with patch('tmp.my_module.MyClass.my_method, return_value=2)
  • Use patch decorators instead of context managers
  • Change patch statement to with patch('tmp.my_module.MyClass') as p: and then inside the with statement, try to set p like so: p.evaluate = Mock(return_value=2)

感谢任何帮助,谢谢.

推荐答案

我找到了一个更好的解决方案.简而言之,我们需要模拟 MyClass 模拟的 return_value.这是工作测试代码:

I've found a much better solution. In short, we need to mock out the return_value of the MyClass mock. Here's the working test code:

import unittest
from unittest.mock import patch, Mock, MagicMock

from tmp import my_module


class MyClassTestCase(unittest.TestCase):

    def test_create_class_call_method(self):
        # Create a mock to return for MyClass.
        m = MagicMock()
        # Patch my_method's return value.
        m.my_method = Mock(return_value=2)

        # Patch MyClass. Here, we could use autospec=True for more
        # complex classes.
        with patch('tmp.my_module.MyClass', return_value=m) as p:
            value = my_module.create_class_call_method()

        # Method should be called once.
        p.assert_called_once()
        # In the original my_method, we would get a return value of 1.
        # However, if we successfully patched it, we'll get a return
        # value of 2.
        self.assertEqual(value, 2)


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

以及成功的结果:

Ran 1 test in 0.002s

OK

这篇关于Python unittest 模拟类和类方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆