【问题标题】:How would you test this function?你将如何测试这个功能?
【发布时间】:2013-07-03 19:25:40
【问题描述】:

假设我们有一个使用终端程序转换将图像转换为另一种格式的函数。代码如下:

def convert_image(src, dest=None, extension='jpg', quality=100):

    # Create default destination - same path just different extension
    if dest is None:
        dest = src.split('.')[0] + '.' + extension

    try:
        subprocess.check_call(['convert', src, '-quality', quality, dest])
    except CalledProcessError:
        raise ImageException('convert', 'Could not convert image')

    return dest

现在我想测试这个函数以验证它是否按预期工作。

最直接的方法可能是只进行单元测试,提供真实图像的路径,并验证是否使用正确的扩展名创建了新图像。但问题是,很难知道创建的图像是否真的是在质量设置为正确值的情况下创建的,而且在测试函数中实际转换真实图像有点尴尬。

如果我这样调用函数:

convert_image('/tmp/myimage.png')

我现在真正感兴趣的是它从这个输入将这个数组作为输入发送到 check_call:

['convert', '/tmp/myimage.png', '-quality', 100, '/tmp/myimage.jpg']

如果是这样,那么我知道该函数针对这种特殊情况做了正确的事情。

因此,如果用于测试目的的函数在我测试时实际上只返回此数组,并且仅在我运行真实程序时转换图像,那将很方便。

但是在所有函数中都有 if 语句来告诉函数为测试做不同的事情可能不是很好,如果我从来没有真正为我的测试函数使用 check_call,那么我什至可能在我的测试代码没有检测到那个代码。

我正在寻找有关如何测试此代码或如何重构它以使其更具可测试性的建议/讨论/提示。我对使用 python 转换图像的更好方法不感兴趣,这个问题纯粹是关于代码可测试性。

那么,如果你可以测试这个功能 - 你会怎么做?

【问题讨论】:

  • 你能模拟对 subprocess.check_call 的调用吗?该函数应该采用您传递的值并在通过或失败时返回有意义的值? theblobshop.com/pymock
  • 是的,我可以。这绝对是一个合理的解决方案。

标签: python unit-testing testing testability


【解决方案1】:

我认为您很聪明地希望断言正确的数组已发送到check_call,使其成为正确的单元测试(而不是同时测试转换应用程序)。为了能够做到这一点,您确实希望拦截对 check_call 的调用。

下面详细介绍了一种方法(请注意,代码未经测试,但应该对技术有所了解)。要做到这一点,首先将调用 check_call 封装在一个单独的函数中,像这样(convert_image 现在调用而不是 subprocess.check_call):

def check_call(args):
    subprocess.check_call(args)

然后,在您的测试中,您可以将check_call 替换为一个不同的函数,该函数断言将传递给subprocess.check_call 的参数。下面显示了测试的示例:

import unittest

class ConvertImageTest(unittest.TestCase):

    def test_convert_image(self):

        # Test version of check_call to do the assertion
        def assert_check_call(args):
            expected = ['convert', '/tmp/myimage.png', '-quality', 100, '/tmp/myimage.jpg']
            self.assertEquals(expected, args)

        # Replace check_call with our test version
        my.module.check_call = assert_check_call

        # Perform the test
        convert_image('/tmp/myimage.png')

使用这种方法也很容易模拟抛出的异常并测试您如何响应。

一个潜在的问题 - 之后您可能不得不反向分配 my.module.check_call - 老实说,不确定是否需要这样做(我想这取决于模块是否在测试之间重新导入,希望是真的)。

【讨论】:

  • 很好的答案,这正是我要找的
【解决方案2】:

您在这里要做的是模拟转换实用程序。然后您可以在没有实际图像文件的情况下进行测试。您的测试将读取传递给模拟实用程序的内容。

换句话说,您测试的是这里发生的实际“进程间通信”的结果。

或者,正如 Brad 建议的那样,模拟 subprocess.check_call,这似乎是一个更好的主意。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-16
    • 1970-01-01
    • 2020-12-28
    • 1970-01-01
    相关资源
    最近更新 更多