模拟类方法时单元测试中出现ModuleNotFoundError

问题描述 投票:0回答:2

我想测试一个类的方法。我想模拟另一个类中的方法。但我总是收到以下错误。

错误

Ran 1 test in 0.005s

FAILED (errors=1)

Error
Traceback (most recent call last):
  File "D:\dev\test_unittest\lib\site-packages\mock\mock.py", line 1343, in patched
    with self.decoration_helper(patched,
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\contextlib.py", line 119, in __enter__
    return next(self.gen)
  File "D:\dev\test_unittest\lib\site-packages\mock\mock.py", line 1325, in decoration_helper
    arg = exit_stack.enter_context(patching)
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\contextlib.py", line 448, in enter_context
    result = _cm_type.__enter__(cm)
  File "D:\dev\test_unittest\lib\site-packages\mock\mock.py", line 1398, in __enter__
    self.target = self.getter()
  File "D:\dev\test_unittest\lib\site-packages\mock\mock.py", line 1573, in <lambda>
    getter = lambda: _importer(target)
  File "D:\dev\test_unittest\lib\site-packages\mock\mock.py", line 1245, in _importer
    thing = __import__(import_path)
ModuleNotFoundError: No module named 'foo'

文件夹结构

foo.py

from parentfolder.handler import Handler

class Foo:
    def __init__(self):
        self.handler = Handler()

    def verify_client(self):
        client = self.handler.get_value('client')
        return client == 'client'

if __name__ == '__main__':
    foo = Foo()
    re = foo.verify_client()
    print(re)

处理程序.py

class Handler:
    def get_value(self, value):
        return value

测试.py

import unittest
import mock
from parentfolder.foo import Foo


class testFoo(unittest.TestCase):
    @mock.patch('foo.Foo.get_value', return_value='client')
    def test_verify_client(self):
        foo = Foo()
        result = foo.verify_client()
        self.assertTrue(result)


if __name__ == "__main__":
    unittest.main()
python unit-testing mocking python-unittest
2个回答
1
投票

您的单元测试代码存在许多问题。

导入路径无效

这就是导致您看到的错误的原因。如果您查看

unittest.mock.patch
的文档,您会发现
target
字符串必须采用可从您调用
patch
的环境(即执行单元的环境)导入的形式测试一下。

无法从该环境直接导入名为

foo
的模块,因为该模块位于
parentfolder
包中。因此包名称必须包含在模块路径中,如下所示:
patch("parentfolder.foo.[...]")

与您通过在导入路径中提供

Foo
来在
test
模块顶部导入
parentfolder
类的原因相同。

不存在的属性

即使您修复了该导入路径,目标字符串的其余部分仍然是错误的,因为

Foo
模块中的
foo
类没有名为
get_value
的属性。这是你的
Handler
类的一个方法。如果你想修补该方法,你需要像这样编写你的目标字符串:
patch("parentfolder.foo.Handler.get_value")

请注意,我不需要编写

handler
模块的路径,因为
Handler
类已导入到
foo
模块中,这意味着当
foo
为 时,它将位于
patch
的命名空间中叫。在这种情况下,相当于这样写:
patch("parentfolder.handler.Handler.get_value")

缺少测试方法参数

使用

patch
作为测试方法的装饰器意味着您必须使用一个附加参数定义它,该参数将由
patch
创建的模拟作为参数您必须向
new
提供
patch
参数。典型的设置如下所示:

@patch("parentfolder.foo.Handler.get_value")
def test_verify_client(self, mock_get_value):
    ...

为什么不使用
unittest.mock

这没什么大不了的。这很奇怪,为什么你要安装一个单独的包,因为自从 Python

mock
以来
unittest
已经成为标准库的
3.3
包的一部分。

完整的工作示例

总而言之,这就是我重写你的

test
模块的方式:

from unittest import TestCase, main
from unittest.mock import MagicMock, patch

from parentfolder.foo import Foo


class FooTestCase(TestCase):
    @patch("parentfolder.foo.Handler.get_value")
    def test_verify_client(self, mock_get_value: MagicMock) -> None:
        mock_get_value.return_value = "client"
        foo = Foo()
        result = foo.verify_client()
        self.assertTrue(result)


if __name__ == "__main__":
    main()

或者,我喜欢导入正在测试的模块本身,然后使用

patch.object
直接修补其命名空间(无需额外导入),这是可能的,因为模块(就像 Python 中的其他所有东西一样)是一个对象,它的命名空间代表该对象的属性。我认为这是个人喜好的问题:

from unittest import TestCase, main
from unittest.mock import MagicMock, patch

from parentfolder import foo as foo_module


class FooTestCase(TestCase):
    @patch.object(foo_module.Handler, "get_value")
    def test_verify_client(self, mock_get_value: MagicMock) -> None:
        mock_get_value.return_value = "client"
        foo = foo_module.Foo()
        result = foo.verify_client()
        self.assertTrue(result)


if __name__ == "__main__":
    main()

0
投票

我也遇到了类似的问题:对我有用的方法: (检查完以上所有内容后)。 导入系统 sys.path('模块名称')

@patch('module_name.Foo.get_value')

© www.soinside.com 2019 - 2024. All rights reserved.