0

我正在尝试为我的单元测试测试函数创建一个 Python Mixin,返回值和sys.stdout. 我希望 Mixin 有一种方法可以作为装饰器来吞噬sys.stdout,但到目前为止我还没有成功。

我的自定义装饰器应该:

  • sys.stdout被测试函数产生的燕子
  • 使用unittest.mock.patch函数作为装饰器来实现
  • 不接受任何输入(保持代码干净)

我对装饰器的尝试:

import io
import sys
import unittest.mock

class StdoutUnittestMixin(unittest.TestCase):
    @unittest.mock.patch('sys.stdout', new_callable=io.StringIO)
    def monkey_patch_stdout(self, original_function, mock_stdout):
        def wrapper_function(*args, **kwargs):
            captured_output = io.StringIO() # Create StringIO object
            sys.stdout = captured_output    # and redirect stdout.
            return_value = original_function(*args, **kwargs) # Call unchanged function.
            sys.stdout = sys.__stdout__     # Reset redirect.
            return return_value
        return wrapper_function

测试功能示例:

def foo(some_str):
    print(some_str)
    return some_str.isnumeric()

在单元测试中期望使用装饰器:

class Testing(unittest.TestCase):
    @monkey_patch_stdout # The decorator wants inputs - I don't want that
    def test_function_outputs_true(self):
        self.assertTrue(foo("123"))

正如预期的那样,我得到:

TypeError: assert_True() missing 1 required positional argument: 'mock_stdout'

因为,我知道mock_stdout需要在那里,因为unittest.mock.patch装饰者需要。

4

1 回答 1

0

我发现有两个问题:1)正如@kdojeteri 通知我的那样,谢谢,使用unittest.patch.mock装饰器会出现猴子补丁逻辑的重复 2)这sys.stdout = sys.__stdout__条线把事情搞砸了。当我将引用保存sys.stdout到一个新变量中,然后用它来重置sys.stdout它时,它就起作用了。

虽然它不能满足我的要求之一,但最终的工作代码如下。

混合方法:

import io
import sys
import unittest.mock

class StdoutUnittestMixin(unittest.TestCase):
        @staticmethod
    def monkey_patch_stdout(original_function):
        """
        :param original_function: Decorated function which is expected to have stdout
        :return: Wrapped function which uses monkey patching for the sys.stdout object
        """
        def wrapper_function(*args, **kwargs):
            captured_output = io.StringIO()                     # Create StringIO object
            my_stdout = sys.stdout                              # Saving reference for sys.stdout
            sys.stdout = captured_output                        # and redirect stdout.
            return_value = original_function(*args, **kwargs)   # Call unchanged function.
            sys.stdout = my_stdout                              # Reset redirect.
            return return_value
        return wrapper_function

测试功能:

def foo(some_str):
    print(some_str)
    return some_str.isnumeric()

测试方法:

class Testing(unittest.TestCase):
    @StdoutUnittestMixin.monkey_patch_stdout
    def test_wrong_input_outputs_false_with_short_str(self):
        self.assertTrue(foo("123"))
于 2019-10-10T08:24:53.913 回答