提供Python测试数据

12

我如何针对大量不同的数据运行相同的测试?

我希望报告所有失败。

例如:

def isEven(number):
    return True # Quite buggy implementation

data = [
    (2, True),
    (3, False),
    (4, True),
    (5, False),
]

class MyTest:
   def evenTest(self, num, expected):
       self.assertEquals(expected, isEven(num))

我找到了一个解决方案,它仅在第一次失败时引发错误: PHPUnit样式的数据提供者在Python单元测试中

如何运行测试以报告所有失败?


2
可能是重复的问题:*Python的unittest和动态创建测试用例。它本身就是一个重复的问题:如何在Python中生成动态(参数化)单元测试?*。为什么?为什么不? - Peter Mortensen
5个回答

12

如果您正在使用 pytest,可以按照以下方式操作:

import pytest

def is_even(number):
    return True # Wuite buggy implementation

@pytest.mark.parametrize("number, expected", [
    (2, True),
    (3, False),
    (4, True),
    (5, False)
])
def test_is_even(number, expected):
    assert is_even(number) == expected

您将获得类似下面这样的内容(已缩短):

/tmp/test_it.py:13: AssertionError
=========== 2 failed, 2 passed in 0.01 seconds ====================

2
也许在其他答案被贡献时,mark parametrize 还不存在,但这似乎是正确的答案。 - Craig Brett

8

一种解决方案是为data中的每个条目创建不同的测试用例实例:

class MyTest(unittest.TestCase):
    def __init__(self, num, expected):
        unittest.TestCase.__init__(self, "evenTest")
        self.num = num
        self.expected = expected
    def evenTest(self):
        self.assertEqual(self.expected, isEven(self.num))

为了让 unittest 知道如何构建测试用例,请在您的模块中添加一个 load_tests() 函数:
def load_tests(loader, tests, pattern):
    return unittest.TestSuite(MyTest(num, expected)
                              for num, expected in data)

8
你应该使用py.test。我认为unittest模块是从JUnit盲目拷贝过来的。无论如何,你可以这样进行hack:
import unittest

data = [
    (2, True),
    (3, False),
    (4, True),
    (5, False)]

# This should be imported from a separate module.
def isEven(number):
    return True # Quite buggy implementation

def create_test_func(num, expected):
    def _test_func(self):
        self.assertEqual(expected, isEven(num))
    return _test_func

class TestIsEven(unittest.TestCase):

    pass

# pyunit isn't Pythonic enough. Use py.test instead
# till then we rely on such hackery
import new
for i, (num, expected) in enumerate(data):
    setattr(TestIsEven, 'test_data_%d'%i, create_test_func(num, expected))

if __name__ == "__main__":
    unittest.main()

输出结果为:

.F.F
======================================================================
FAIL: test_data_1 (__main__.TestIsEven)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "untitled-1.py", line 15, in _test_func
    self.assertEqual(expected, isEven(num))
AssertionError: False != True

======================================================================
FAIL: test_data_3 (__main__.TestIsEven)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "untitled-1.py", line 15, in _test_func
    self.assertEqual(expected, isEven(num))
AssertionError: False != True

----------------------------------------------------------------------
Ran 4 tests in 0.000s

FAILED (failures=2)

使用这种方法,您可以添加更多的细节,例如在失败时打印调试信息等。


1
+1,我认为unittest模块是从junit盲目复制过来的,而且不仅仅是这个模块从Java中复制过来的。 - mouad

5
您可能正在寻找类似以下内容的东西:
import unittest


def is_even(number):
    return True # Quite buggy implementation


class TestCase(unittest.TestCase):
    def setUp(self):
        self.expected_output = [
            (2, True),
            (3, False),
            (4, True),
            (5, False)
        ]

    def test_is_even(self):
        real_res = []

        for arg, _ in self.expected_output:
            real_res.append((arg, is_even(arg)))

        msg_error = '\nFor %s Expected %s Got %s'
        msg = []
        for res1, res2 in zip(real_res, self.expected_output):
            if res1[1] != res2[1]:
                msg.append(msg_error % (res1[0], res1[1], res2[1]))


        self.assertEqual(real_res, self.expected_output, "".join(msg))


if __name__ == '__main__':
    unittest.main()

输出:

F
======================================================================
FAIL: test_is_even (__main__.TestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 29, in test_example
    self.assertEqual(real_res, self.expected_output, ''.join(msg))
AssertionError:
For 3 Expected True Got False
For 5 Expected True Got False

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

如果我有50个测试,其中10个失败了,那么很难找出问题出在哪里。对于一小组简单的测试数据,这将非常方便和有用。谢谢 :) - jinowolski
3
没问题,我们很高兴可以自定义错误信息,我编辑了我的回答,不知道这是否对你有用。 - mouad
3
@Anurag:在我看来,一个(简单的)函数对应一个测试是理所当然的。即使我们想要将测试拆分,它们也应该全部包含在一个测试用例中。如果这只是一个大型包中的一部分,我无法想象将其拆分为多个测试用例,但这只是我的个人意见 :) - mouad

0
import unittest

data = [
    (2, True),
    (3, False),
    (4, True),
    (5, False)]

# This should be imported from a separate module.
def isEven(number):
    return True # Quite buggy implementation


class TestIsEven(unittest.TestCase):
    def test_is_even(self):
        for num, expected in data:
            self.assertEqual(expected, isEven(num))

这会在第一个错误时引发 - 请再次阅读问题的结尾。 - Sven Marnach

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接