简体   繁体   English

Python unittest.mock:调用了已修补的类方法,但断言失败

[英]Python unittest.mock: patched class method called but assertion fails

I'm trying to mock several components of a utility class. 我正在尝试模拟实用程序类的几个组件。 While assert_called() is ok for one method it fails for an other but I am sure that both are called. 尽管assert_called()对于一种方法是可以的,但对于另一种方法却是失败的,但是我敢肯定两者都可以被调用。 I'm running Python 3.7.3 on Windows 10. 我正在Windows 10上运行Python 3.7.3。

I have stripped down my scenario to the essentials. 我已将方案简化为基本要素。 The utility class ( util.py ): 实用程序类( util.py ):

class api:

    @staticmethod    
    def send(data):
        print("sending %s" % data)

    class logger:

        @staticmethod
        def info(s):
            print("INFO: %s" % s)

        @staticmethod
        def error(s):
            print("ERROR: %s" % s)

The unit test variant that works fine: 正常工作的单元测试变体:

import unittest
from unittest.mock import patch
from util import api

def do_something():
    api.logger.info("doing something")
    api.send("some data")

@patch("util.api.logger")
class Test(unittest.TestCase):

    def test_do_something(self, mock_logger):
        do_something()
        mock_logger.info.assert_called()

The one that fails: 失败的那一个:

import unittest
from unittest.mock import patch
from util import api

def do_something():
    api.logger.info("doing something")
    api.send("some data")

@patch("util.api")
class Test(unittest.TestCase):

    def test_do_something(self, mock_api):
        do_something()
        mock_api.send.assert_called()

When running the tests I am getting both print() outputs: 运行测试时,我同时获得两个print()输出:

INFO: doing something
sending some data

so I'm sure that both methods are called. 因此,我确定两种方法都可以调用。

Very likely I'm making some stupid dummy mistake because I'm really new to python... 很可能我犯了一些愚蠢的虚拟错误,因为我真的是python新手...

Some more background: 更多背景:

In my stripped down scenario do_something() is just a stand-in for a set of functions that are the object of my test and that in my real scenario are actually defined in a separate python file. 在我精简的场景中, do_something()只是一组功能的替代,这些功能是我测试的对象,而在我的实际场景中,这些功能实际上是在单独的python文件中定义的。 In the production environment it runs in the context of a framework that provides the utility class api. 在生产环境中,它在提供实用程序类api的框架的上下文中运行。 In my test environment util.py itself is a mock of the production api. 在我的测试环境中, util.py本身是生产api的模拟。 The py file to be tested (instead of def do_something(): ... ) is loaded like so: 像这样加载要测试的py文件(而不是def do_something(): ... ):

path = os.getcwd() + "<local path to py file to be tested>"
globals().update({ **runpy.run_path(path, init_globals=globals()), **globals() })

Therefore, I can't modify the do_something() code in the test scenario. 因此,我无法在测试方案中修改do_something()代码。

This was a tricky one. 这是一个棘手的问题。

The issue is that the api class in imported by the test class well before it is patched. 问题在于,修补程序之前,测试类已导入了api类。 From the docs for patch : 从文档的补丁

The target is imported when the decorated function is executed, not at decoration time. 在执行装饰功能时(而不是在装饰时)导入目标。

Since you import the api class first thing in the test class, that guy is not getting patched when you run the test. 由于您首先将api类导入测试类中,因此在运行测试时不会修补该家伙。 Python thinks it's already imported. Python认为它已经被导入。

Note this is not the case with the logger class since you never import it. 请注意, logger类不是这种情况,因为您从不会导入它。 If you had, the first test would've failed as well. 如果有的话,第一个测试也会失败。

Simplest fix - move the import inside the test function. 最简单的修复-将导入移动到测试功能中。

def do_something():
     from util import api
     api.logger.info("doing something")
     api.send("some data")

Now patch will do the importing - so the mock is placed in the test scope instead of the actual class. 现在, patch将执行导入-因此将模拟放置在测试范围中,而不是实际的类中。

Finally got it after having found this SO question - I wasn't aware that you can also patch individual functions/class methods (not mentioned in the unittest.mock.patch reference ). 找到这个SO问题之后终于得到了它-我不知道您也可以修补单个函数/类方法( unittest.mock.patch参考中未提及)。 So this works: 所以这有效:

import unittest
from unittest.mock import patch
from util import api

def do_something():
    api.logger.info("doing something")
    api.send("some data")

@patch("util.api.send")
@patch("util.api.logger")
class Test(unittest.TestCase):

    def test_do_something(self, mock_logger, mock_send):
        do_something()
        mock_send.assert_called()
        mock_logger.info.assert_called()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM