【问题标题】:Mocking an object in a distant module模拟远程模块中的对象
【发布时间】:2020-08-04 15:40:09
【问题描述】:

我在 ClassA 的一个类上运行了一些单元测试。此类具有调用ClassB 的方法。 ClassAClassB 位于单独的模块中,class_A.pyclass_B.py。这两个模块都有logger 函数,在该模块中全局可用。

在测试ClassA 的方法期间,我想禁止记录日志消息。使用mock.patch 可以直接为class_A.py 中的记录器执行此操作。但我还没有找到修补class_B.py 中的记录器的方法。

这里是一些示例代码:

unittest_file.py

import unittest
import class_A

class TestClassA(unittest.TestCase):

    def setUp(self):
        pass

    def test_method(self):
        my_class_A = class_A.ClassA()
        my_class_A.run_method()

    def tearDown(self):
        pass

class_A.py

import logging
from class_B import ClassB

logger = logging.getLogger(__name__)

class ClassA:

    run_method(self):
        my_class_B = ClassB()
        my_class_B.do_something()
        logger.info("Running method on Class A")

class_B.py

import logging

logger = logging.getLogger(__name__)

class ClassB:

    do_something(self):
        logger.info("Doing something on Class B")

要修补class_A.py 中的记录器,我可以将@patch('__main__.unittest_file.ClassA.logger', autospec=True) 作为装饰器添加到test_method() 方法中。 但我也无法直接访问 class_B.py 中的记录器来修补它。

我最接近的解决方案是这样的:

@patch(eval('__main__.unittest_file.class_A.ClassB.__module__')['logger'])

但应用于模块名称的eval() 函数不会保留导入位置,因此它不会修补正确命名空间中的记录器。

我知道我可以通过将class_A.py 中的import 语句更改为import class_B 而不是from class_B import ClassB 来轻松解决这个问题,但我不想编辑class_A.pyclass_B.py 中的代码,所以这不是我要找的答案。

我也知道可以通过禁用日志记录来解决这个问题,如this question,但我更感兴趣的是能够微调我对要测试的代码的不同部分的控制,所以这不是答案我也在找。

我的问题是: 有没有一种方法可以让我从导入的对象导航到其父模块中的其他对象,同时保持在同一个命名空间中?

【问题讨论】:

  • 所以您只想修补记录器,而保留其他所有内容?
  • 现在,是的,我只是想控制日志输出。

标签: python unit-testing import monkeypatching


【解决方案1】:

对我来说,只需修补 class_A.loggerclass_B.logger 即可,例如

import unittest
from unittest import mock

class TestCaseA(unittest.TestCase):

    @mock.patch('class_A.logger')
    @mock.patch('class_B.logger')
    def test_method(self, mockedA, mockedB):
        my_class_A = ClassA()
        my_class_A.run_method()

两个记录器都不再输出日志消息。

【讨论】:

  • 但是unittest_file.py模块对class_B一无所知,所以我不能直接修补它。 class_B 的记录器仅通过 class_A 的命名空间引用。
  • 我刚试了一下,效果很好。你试过了吗?
  • unittest.mock 的参数作为字符串传递,并通过在运行测试时导入 dottpath 中的模块来解析。这将起作用,除非在运行测试时在 pythonpath 中找不到 class_B.py 文件。
  • 这是一个非常不现实的场景。 @berkelem 有用吗?
  • 是的,它适用于我发布的示例代码,但不适用于我原来的问题。经过一番挖掘后,我意识到我的示例代码并不能准确反映我遇到的问题,因为在我的情况下,我不是直接调用记录器,而是通过装饰器函数。而且修补装饰器并不容易,因为它在导入时应用于函数。我会接受你的回答,因为它回答了所问的问题。但以防万一有人遇到和我一样的麻烦,这里是帮助我解决问题的答案:stackoverflow.com/a/37890916/4844311
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-12
  • 2020-11-08
  • 1970-01-01
  • 1970-01-01
  • 2017-04-07
  • 1970-01-01
相关资源
最近更新 更多