模拟打印但允许在测试中使用它

时间:2014-12-16 20:30:36

标签: python-3.x mocking unit-testing

可以通过以下方式模拟打印:

import unittest
import builtin

class TestSomething(unittest.TestCase):
    @mock.patch('builtins.print')
    def test_method(self, print_):
        some_other_module.print_something()

但是这意味着在python调试控制台(pydev调试器)和单元测试方法本身print中不能使用。这很不方便。

有没有办法只在print而不是在测试模块中模仿some_other_module方法?

回避这一点的一种方法是将测试模块中print的使用与只调用print的其他函数交换,如果事实证明没有更好的解决方案,我可以这样做。

2 个答案:

答案 0 :(得分:6)

是的,你可以! ...但仅仅因为你使用的是Python 3.在Python 3中print是一个函数,您可以在不更改名称的情况下重写它。为了理解最终解决方案,我将逐步描述它以获得最终灵活且非侵入性的解决方案。

仪器模块

诀窍是在你的模块顶部添加你将测试一行如下:

print = print

现在你可以只修补你的模块的print。我写了一个测试用例mock_print_module.py

print = print

def print_something():
    print("print something")

测试模块(我使用autospec=True只是为了避免像mock_print.asser_called_with这样的错误):

from unittest import TestCase
from unittest.mock import patch
import mock_print_module

class MyTestCase(TestCase):
    @patch("mock_print_module.print",autospec=True)
    def test_something(self,mock_print):
        mock_print_module.print_something()
        mock_print.assert_called_with("print something")

我不想更改我的模块,只是修补打印而不丢失功能

您只需使用patch "builtins.print"的属性,就可以side_effect使用patch而不会失去打印功能:

@patch("builtins.print",autospec=True,side_effect=print)
def test_somethingelse(self,mock_print):
    mock_print_module.print_something()
    mock_print.assert_called_with("print something")

现在,您可以在不丢失日志记录和pydev调试器的情况下跟踪您的打印调用。这种方法的缺点是你必须对抗很多噪音,以检查你感兴趣的打印电话。此外,您无法选择要修补的模块和不适用的模块。

两种模式不能一起工作

您不能同时使用这两种方式,因为如果您在模块中使用print=print,则会在加载模块时将builtins.print保存在print变量中。现在,当您修补builtins.print时,模块仍然使用原始保存的模块。

如果你有机会同时使用它们,你必须包装原始印刷品而不是记录它。实现它的方法是使用以下代替print=print

import builtins
print = lambda *args,**kwargs:builtins.print(*args,**kwargs)

最终解决方案

我们是否真的需要修改原始模块才有机会修补其中的所有打印调用?不,我们无需更改模块即可完成测试。我们唯一需要的是在模块中注入一个本地print函数来覆盖builtins的一个:我们可以在测试模块而不是要测试的模块中完成。我的例子将成为:

from unittest import TestCase
from unittest.mock import patch
import mock_print_module

import builtins
mock_print_module.print = lambda *args,**kwargs:builtins.print(*args,**kwargs)

class MyTestCase(TestCase):
    @patch("mock_print_module.print",autospec=True)
    def test_something(self,mock_print):
        mock_print_module.print_something()
        mock_print.assert_called_with("print something")

    @patch("builtins.print",autospec=True,side_effect=print)
    def test_somethingelse(self,mock_print):
        mock_print_module.print_something()
        mock_print.assert_called_with("print something")

mock_print_module.py可以是干净的原始版本,只有:

def print_something():
    print("print something")

答案 1 :(得分:6)

@ michele的“最终解决方案”有一个更清晰的替代方案,适用于我的情况:

from unittest import TestCase
from unittest.mock import patch

import module_under_test


class MyTestCase(TestCase):
    @patch('module_under_test.print', create=True)
    def test_something(self, print_):
        module_under_test.print_something()
        print_.assert_called_with("print something")