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

sim*_*ack 4 unit-testing mocking python-3.x

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

import unittest
import builtin

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

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

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

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

sim*_*ack 7

@ 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")
Run Code Online (Sandbox Code Playgroud)


Mic*_*ico 6

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

仪器模块

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

print = print
Run Code Online (Sandbox Code Playgroud)

现在你可以修补print你的模块了.我写了一个测试用例,其中mock_print_module.py:

print = print

def print_something():
    print("print something")
Run Code Online (Sandbox Code Playgroud)

和测试模块(我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")
Run Code Online (Sandbox Code Playgroud)

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

您只需使用以下属性即可使用patchon "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")
Run Code Online (Sandbox Code Playgroud)

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

两种模式不能一起工作

因为如果你使用不能同时使用的方式共同print=print您的模块中保存builtins.printprint变量在加载模块的时间.现在,当您修补builtins.print模块时,仍然使用原始保存的模块.

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

import builtins
print = lambda *args,**kwargs:builtins.print(*args,**kwargs)
Run Code Online (Sandbox Code Playgroud)

最终的解决方案

我们真的需要修改原始模块才有机会修补其中的所有打印调用吗?不,我们无需更改模块即可完成测试.我们唯一需要的是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")
Run Code Online (Sandbox Code Playgroud)

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

def print_something():
    print("print something")
Run Code Online (Sandbox Code Playgroud)