你会如何测试这个功能?

时间:2013-07-03 19:25:41

标签: python unit-testing testing testability

假设我们有一个使用终端程序转换将图像转换为另一种格式的功能。这是代码:

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转换图像的更好方法不感兴趣,这个问题纯粹是关于代码可测试性的。

所以,如果你有能力测试这个功能 - 你会怎么做?

2 个答案:

答案 0 :(得分:2)

我认为你很聪明地想要断言正确的数组被发送到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 - 说实话不确定是否需要(我猜这取决于模块是否在测试之间重新导入,哪一个会希望是真的。)

答案 1 :(得分:0)

你想要做的是模拟转换实用程序。然后你可以测试没有实际的图像文件。您的测试将读取传递给模拟实用程序的内容。

换句话说,您测试了正在进行的实际“进程间通信”的结果。

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