2010-02-03 38 views
6

我需要为某些python类创建单元测试。我有一个输入数据库和预期结果数据库,应该由UUT生成这些输入数据。如何编写一个单元测试,其中每个测试用例有不同的输入但是相同?

这里是我想要做的伪代码:这个使用单元测试包或者是有一些更好的测试包,为此

for i=1 to NUM_TEST_CASES: 
    Load input for test case i 
    execute UUT on the input and save output of run 
    Load expected result for test case i 
    Compare output of run with the expected result 

我能实现吗?

+0

为了澄清你的问题,你的意思是像PHPUnit中的数据提供者? http://www.phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers – majelbstoat 2010-02-04 04:30:32

回答

4

你描述测试的方式对单元测试来说是一个奇怪的匹配。单元测试通常不会 - 加载测试数据或来自外部文件的其余结果。一般来说,它只是在单元测试中进行硬编码。

这并不是说你的计划不会奏效。这只是说它是非典型的。

你有两种选择。

  1. (我们做什么)。编写一个脚本,它执行“为测试用例i加载输入”和“为测试用例i加载预期结果”。使用它来生成所需的单元测试代码。 (我们使用Jinja2模板从源文件编写Python代码。)

    然后删除源文件。是的,删除它们。他们只会迷惑你。

    你剩下的是“典型”形式的正确单元测试文件,包含测试用例和预期结果的静态数据。

  2. 编写您的setUp方法来执行“为测试用例i加载输入”和“为测试用例i加载预期结果”。编写你的test方法来锻炼UUT。

它可能看起来像这样。

class OurTest(unittest.TestCase): 
    def setUp(self): 
     self.load_data() 
     self.load_results() 
     self.uut = ... UUT ... 
    def runTest(self): 
     ... exercise UUT with source data ... 
     ... check results, using self.assertXXX methods ... 

想要运行这么多次吗?一种方式来做这样的事情。

class Test1(OurTest): 
    source_file = 'this' 
    result_file = 'that' 

class Test2(OutTest): 
    source_file= 'foo' 
    result_file= 'bar' 

这将允许unittest主程序找到并运行您的测试。

+0

谢谢。我想我会选择1. Jinja2看起来很有趣。他们使用什么许可证? – 2010-02-04 10:58:09

+1

@zr:“他们使用什么许可证?”为什么要问我? PyPi页面说:http://pypi.python.org/pypi/Jinja2。 – 2010-02-04 11:40:03

0

也许你可以使用doctest这个。了解你的投入和产出(并且能够案件编号映射到一个函数名),你应该能够产生一个文本文件是这样的:

>>> from XXX import function_name1 
>>> function_name1(input1) 
output1 
>>> from XXX import function_name2 
>>> function_name2(input2) 
output2 
... 

,然后只用doctest.testfile('cases.txt')。这可能值得尝试。

3

我们做这样的事情才能运行实际上是什么整合(回归)的unittest框架(实际上它们其中一个内部定制为我们提供了巨大的好处,例如在群集上并行运行试验中测试机器等等 - 这种定制的巨大附加价值是我们如此热衷于使用框架的原因)。

每个测试都在文件中表示(在该测试中使用的参数,然后是预期的结果)。我们integration_test读取目录下的所有这样的文件,分析它们的每一个,然后调用:

def addtestmethod(testcase, uut, testname, parameters, expresults): 
    def testmethod(self): 
    results = uut(parameters) 
    self.assertEqual(expresults, results) 
    testmethod.__name__ = testname 
    setattr(testcase, testname, testmethod) 

我们先从一个空的测试用例类:

class IntegrationTest(unittest.TestCase): pass 

,然后调用addtestmethod(IntegrationTest, ...在一个循环我们正在读取所有相关文件并解析它们以获取测试名称,参数和表达式。

最后,我们称之为内部专门的测试运行者,它负责繁重的工作(分布在集群中可用机器上的测试,收集结果等)。我们不想重塑那个富有附加价值的车轮,所以我们正在制作一个测试用例,尽量接近典型的“手工编码”测试用例,以便“欺骗”测试跑步者,使其适合我们; - )。

除非您有具体的原因(好的测试者或类似人员)使用unittest的方法来进行(集成)测试,否则您可能会发现使用不同的方法可以让您的生活更简单。然而,这个方法非常可行,我们对它的结果非常满意(主要包括大型集成/回归测试套件的快速运行!)。

+1

我发现自己使用“单元测试”来表示“我使用unittest或nUnit编写的测试”,即使它们通常不是单元测试。 – 2010-02-04 04:44:45

0

您可能还想看看my answerthis question。我试图做回归测试而不是单元测试,但单元测试框架对两者都有好处。

在我的情况下,我有大约十几个输入文件,覆盖了不同用例的公平传播,并且我有大约六个测试函数,我想每个函数都要调用它们。

除了写入72个不同的测试,其中大部分与输入参数和结果数据相同之外,我创建了一个结果字典(其中键为输入参数,值为每个函数的结果字典)测试)。然后我编写了一个单独的TestCase类来测试6个函数中的每一个,并通过多次将TestCase添加到测试套件来复制12个测试文件。

1

对我来说,好像pytest只是你需要的东西。

你可以parametrise tests这样相同的测试运行多次你有输入,它只需要一个装饰器(没有循环等)。

这里有一个简单的例子:

import pytest 
@pytest.mark.parametrize("test_input,expected", [ 
    ("3+5", 8), 
    ("2+4", 6), 
    ("6*9", 42), 
]) 
def test_eval(test_input, expected): 
    assert eval(test_input) == expected 

这里parametrise有两个参数 - 参数是一个字符串的名字,这些参数可迭代的值。

test_eval然后将被调用一次列表的每个元素。

相关问题