python3模拟不适用于所有路径 [英] python3 mock doesn't work for all paths

查看:97
本文介绍了python3模拟不适用于所有路径的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

生产文件(production_file.py)是:

The Production file (production_file.py) is:

class MyError(Exception):
    pass

class MyClass:
    def __init__(self):
        self.value = None

    def set_value(self, value):
        self.value = value

    def foo(self):
        raise RuntimeError("error!")


class Caller:
    def bar(self, smth):
        obj = MyClass()
        obj.set_value(smth)

        try:
            obj.foo()
        except MyError:
            pass

        obj.set_value("str2")
        obj.foo()

测试文件(test.py):

Test file (test.py):

import unittest

from unittest.mock import patch
from unittest.mock import call
from production_file import MyClass, Caller

class MyTest(unittest.TestCase):
    def test_caller(self):
        with patch('production_file.MyClass', autospec=MyClass) as MyClassMock:
            my_class_mock_obj = MyClassMock.return_value
            my_class_mock_obj.foo.side_effect = [MyError("msg"), "text"]

            caller = Caller()
            caller.bar("str1")

            calls = [call("str1"), call("str2")]

            my_class_mock_obj.set_value.assert_has_calls(calls)

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

以上方法有效.但是,如果我将生产类(MyError,MyClass,Caller)移到测试文件中,并将补丁更新为:

This above works. But if I move the production classes (MyError, MyClass, Caller) into the test file, and update patch to:

with patch('test.MyClass', autospec=MyClass) as MyClassMock:

然后不再模拟实例方法"foo".

then the instance method "foo" is no longer mocked.

有人知道为什么会这样吗?

Does anybody have any idea why that is?

我还遇到了一些更复杂的代码的类似问题,其中生产代码在my_package/src/production_file.py中,而测试在my_package/tests/test_file.py中. Python的路径没有错误,路径是正确的,但模拟仍然无效.

I have also experienced a similar problem with some more complex code, where the production code is in my_package/src/production_file.py while the test is in my_package/tests/test_file.py. Python yields no error for the path, the path is correct, but still the mock doesn't work.

推荐答案

如果您将test.py作为__main__运行,则不是test.MyClass,而是__main__.MyClass,或者在两种情况下都是__name__+".MyClass".

If you are running test.py as __main__ then it is not test.MyClass it would be __main__.MyClass, or in both cases __name__+".MyClass".

通过添加一条打印语句,我可以确定所使用的类和所修补的类是否不同:

I was able to determine that the class used and the class patched were different by adding a print statement:

class Caller:
    def bar(self, smth):
        print(MyClass) #lets see what we are actually making an instance of...
        obj = MyClass()
        ...

将补丁程序应用于正在使用的类时,您会看到类似以下内容的内容:

When the patch is applied to the class that this is using you would see something like this:

<MagicMock name='MyClass' spec='MyClass' id='4387629656'>

但是当将班级移入test.py时,您会看到类似以下内容的内容:

But when the class in moved into test.py you will see something like:

<class '__main__.MyClass'>

哪个表示:

  1. 没有对MyClass(至少是用于测试的补丁)应用补丁.
  2. 需要修补的类的名称为__main__.MyClass
  1. There was no patching applied to MyClass (at least the one that is used for the test.)
  2. The name of the class that needs to be patched is __main__.MyClass

但是,由于这样的设置,您的更复杂的情况"很可能无法正常工作:

However It is quite likely that your "more... complicated situation" is not working because of a setup like this:

from production_file import MyClass

class MyError(Exception):
    pass


class Caller:
    def bar(self, smth):
        print(MyClass)
        obj = MyClass()
        ...

class MyTest(unittest.TestCase):
    def test_caller(self):
        with patch('production_file.MyClass', autospec=MyClass) as MyClassMock:
            ...

在这种情况下,正在修补production_file.MyClass并从production_file导入MyClass,因此正在修补正确的类,但输出仍然是:

In this case production_file.MyClass is being patched and MyClass is being imported from production_file so the correct class is being patched but still the output is:

<class 'production_file.MyClass'>

这是因为该类直接导入到本地名称空间,因此,当将修补程序应用于production_file时,本地名称空间仍然不受影响,我们可以检查该修补程序是否实际应用于:

This is because the Class was directly imported to the local namespace, so when the patch is applied to the production_file the local namespace is still unaffected, we can check that the patch was actually applied with:

...
def bar(self, smth):
    print(MyClass)
    from production_file import MyClass as pf_MyClass
    print(pf_MyClass)
...


#output:
<class 'production_file.MyClass'>
<MagicMock name='MyClass' spec='MyClass' id='4387847136'>

在这种情况下,您只需要导入模块,而不是直接导入类.然后,一旦应用了补丁,您将直接在文件中使用它:

If this is the case you just need to import the module, not the class directly. Then once the patch is applied you will be using it right from the file:

import production_file

...
class Caller:
    def bar(self, smth):
        print(production_file.MyClass)
        obj = production_file.MyClass()
        ...

class MyTest(unittest.TestCase):
    def test_caller(self):
        with patch('production_file.MyClass', autospec=MyClass) as MyClassMock:
            ...

这篇关于python3模拟不适用于所有路径的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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