【问题标题】:Mock returning an ImportError when patching a module that's imported修补导入的模块时模拟返回 ImportError
【发布时间】:2015-12-22 04:09:06
【问题描述】:

我在模拟函数时遇到了一些问题。所述函数已导入并在run_parsers.py 中使用,我得到了

ImportError: 'No module named run_parsers'

当我尝试mock.patch run_parsers.py 时。

这是我在test_run_parsers.py中的测试代码

from .. import run_parsers # Used in all my other tests.

def test_node_data_parser_throws_exception(self):
    def parser():
        return NotImplementedError()

    with mock.patch("run_parsers.get_node_paths") as node_paths:
        node_paths.return_value = "node_1"
        run_parsers.get_node_data(parser, "/a/path")

这是我的存储库结构

control_scripts
├── __init__.py
├── README.md
├── run_all_parsers.py
├── run_parsers.py
└── tests
    ├── __init__.py
    ├── test_run_parsers.py

According to this tutorial I'm supposed to mock where the function is imported.这就是为什么我试图模拟调用模块而不是定义 get_node_paths 的模块

【问题讨论】:

  • 我看到您在嘲笑“run_parsers.get_node_paths”,但您没有调用该函数,而是调用了“run_parsers.get_node_data”。那是错字吗?还是“run_parsers.get_node_data”调用了“run_parsers.get_node_paths”?
  • 另一个问题:您是否尝试(作为测试用例)使用绝对导入“import run_parsers”并确保目录“control_scripts”在您的 sys.path 上?这只是为了首先测试模拟功能是否按预期工作,然后您可以解决导入问题。这就是我通常尝试解决这些问题的方式。
  • @SteveMisuta 这不是错字。我正在尝试测试get_node_dataget_node_data 调用的函数之一是get_node_paths。我还没有检查 control_scripts 是否在路径中。我得去看看,但我很确定不是。
  • run_parser 不在您的类路径中,因此补丁无法解决它。改用mock.patch("control_script.run_parsers.get_node_paths")
  • github.com/la10736/SimpleScratchExtension/blob/master/scratch/… 第 726 行(我希望如此)。是一个非常接近您的项目结构的示例。

标签: python unit-testing mocking


【解决方案1】:

我不确定这是否完全复制了您的设置,但这里有一个对我有用的简单测试用例。

目录设置为:

c:\work
    \control
        __init__.py
        scripts.py
        \tests
            __inti__.py
            mytests.py

and c:\work is on sys.path

在模块scripts.py中:

def identity(x):
    return x

def do_identity(x):
    return identity(x)

在 mytests.py 中:

import unittest
from unittest.mock import patch
from control import scripts

class MyTest(unittest.TestCase):

    def test_patch(self):

        with patch('control.scripts.identity') as mymock:
            mymock.return_value = 99
            self.assertEqual(scripts.do_identity(1), 99)

    def test_no_patch(self):

            self.assertEqual(scripts.do_identity(1), 1)            

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

所以我在这里要做的是模拟函数“do_identity”调用的函数“identity”。这两个函数都在“脚本”模块中。此测试运行没有错误或失败。

我可以从任何目录运行它:

c:\any_directory> python c:\work\control\tests\mytests.py

【讨论】:

  • 确认我需要使用绝对导入并且他的父目录需要在路径上。在这种情况下可能有一种使用相对导入的方法,但这是我最终使用的解决方案。
  • 太棒了!最近我一直在使用完整路径绝对导入语句,尤其是在测试中。然后我可以在任何地方以程序或模块的形式运行测试,以及交互式工作。此方法也适用于 Python 2 和 3,即使它们使用不同的导入方案。您只需将内容构建为 top\pkg\sub1\sub2\mod1.py 并确保 top 在 sys.path 上。然后像'from pkg.sub1.sub2 import mod1'或类似的导入将始终有效。相对导入有优势,但我似乎最终会遇到麻烦,而且我没有时间调试导入失败。
  • 遇到了同样的问题,因为我正在从包中导入一个模块(使用from x import y),然后尝试像@patch(y...) 一样模拟它...The docs 提到使用package.module.className 表示“目标”,但没有真正明确指出“包”是必需的(即必须使用绝对导入路径)
【解决方案2】:

对于更复杂的项目结构(或者如果你想让模拟部分更短),我想出了一个棘手的解决方案,因为我需要将逻辑和 UI 分开。

我的结构看起来像这样:

├───sourceroot
│   ├───python_pkg
│   │   ├───__init__
│   │   └───subpkg
│   │       ├───__init__
│   │       ├───logic
│   │       │   ├───lpkg1
│   │       │   │   ├───__init__
│   │       │   │   ├───file1.py
│   │       │   │   └───file2.py
│   │       │   ├───lpkg2
│   │       │   │   ├───__init__
│   │       │   │   ├───file3.py
│   │       │   │   └───file4.py
│   │       │   ├───__init__
│   │       │   └───file.py
│   │       └───ui
│   │           ├───uipkg3
│   │           │   ├───__init__
│   │           │   ├───file_ui1.py
│   │           │   └───file_ui2.py
│   │           ├───uipkg4
│   │           │   ├───__init__
│   │           │   ├───file_ui3.py
│   │           │   └───file_ui4.py
│   │           ├───__init__
│   │           └───file_ui.py
│   └───packages
│       └───some_3rd_party_packages
├───srcfiles_from_3rd_parties
└───tests
    └───unit_tests_py
        ├───__init__
        └───test.py

我不得不参考 test.py 中的 file.py 和 file1.py。 要从 test.py 文件中查看 sourceroot,我将以下内容写入 sourceroot/tests/unit_test_py/__init__

import sys
import os
sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..')))

在初始化并将 sourceroot 添加到路径(Python 将在其中查找)之后,test.py 类就可以编辑了:

之前:

import mock
from sourceroot.python_pkg.subpkg.logic import file
from sourceroot.python_pkg.subpkg.logic.lpkg1.file1 import SomeClassFromFile1 as SCF1

class Test_test1(object):
    def test_first(self, mocker):
        mocker.patch('sourceroot.python_pkg.subpkg.logic.lpkg1.file1.some_function_which_SCF1_calls')
        mocker.patch('sourceroot.python_pkg.subpkg.logic.file.SomeClassInFile.some_function_which_SomeClassInFile_calls')
        SCF1.some_function_in_SCF1()
        expected_information = True
        assert SCF1.is_something() == expected_information


之后:

import mock
from sourceroot.python_pkg.subpkg.logic import file
from sourceroot.python_pkg.subpkg.logic.lpkg1.file1 import SomeClassFromFile1 as SCF1

class Test_test1(object):
    def test_first(self, mocker):
        mocker.patch.object(SCF1, 'some_function_which_SCF1_calls')
        mocker.patch.object(file.SomeClassInFile, 'some_function_which_SomeClassInFile_calls')
        SCF1.some_function_in_SCF1()
        expected_information = True
        assert SCF1.is_something() == expected_information


【讨论】:

    猜你喜欢
    • 2020-09-30
    • 2012-10-04
    • 2023-02-22
    • 2021-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多