我有一些测试数据,想为每个项目创建一个单元测试。我的第一个想法是这样做的:
import unittest
l = [["foo", "a", "a",], ["bar", "a", "b"], ["lee", "b", "b"]]
class TestSequence(unittest.TestCase):
def testsample(self):
for name, a,b in l:
print "test", name
self.assertEqual(a,b)
if __name__ == '__main__':
unittest.main()
这样做的缺点是它在一个测试中处理所有数据。我想在飞行中为每个项目生成一个测试。有什么建议吗?
元编程很有趣,但它也会碍事。这里的大多数解决方案都很难:
有选择地启动测试
指向给出测试名称的代码
所以,我的第一个建议是遵循简单/显式路径(适用于任何测试运行程序):
import unittest
class TestSequence(unittest.TestCase):
def _test_complex_property(self, a, b):
self.assertEqual(a,b)
def test_foo(self):
self._test_complex_property("a", "a")
def test_bar(self):
self._test_complex_property("a", "b")
def test_lee(self):
self._test_complex_property("b", "b")
if __name__ == '__main__':
unittest.main()
既然我们不应该重复,我的第二个建议建立在Javier的回答之上:接受基于属性的测试。假设库:
“在生成测试用例方面比我们人类更加无情地迂回”
会提供简单的计数例子吗
与任何测试运行程序一起工作
具有更多有趣的特性(统计数据、额外的测试输出……)
类TestSequence (unittest.TestCase):
st.text @given (st.text () ()
Def test_complex_property(self, a, b):
self.assertEqual (a, b)
为了测试您的特定示例,只需添加:
@example("a", "a")
@example("a", "b")
@example("b", "b")
为了只运行一个特定的示例,您可以注释掉其他示例(提供的示例将首先运行)。你可能想要使用@given(st.nothing())。另一种选择是将整个区块替换为:
@given(st.just("a"), st.just("b"))
好的,您没有不同的测试名称。但也许你只需要:
被测属性的描述性名称。
哪个输入会导致失败(伪造的例子)。
有趣的例子
从Python 3.4开始,已经为此目的向unittest引入了子测试。详细信息请参见文档。TestCase。subTest是一个上下文管理器,它允许在测试中隔离断言,以便用参数信息报告失败,但它不会停止测试执行。下面是文档中的例子:
class NumbersTest(unittest.TestCase):
def test_even(self):
"""
Test that numbers between 0 and 5 are all even.
"""
for i in range(0, 6):
with self.subTest(i=i):
self.assertEqual(i % 2, 0)
测试运行的输出是:
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=1)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=3)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=5)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
这也是unittest2的一部分,因此可用于早期版本的Python。
使用unittest(从3.4开始)
从Python 3.4开始,标准库unittest包具有subTest上下文管理器。
参见文档:
26.4.7. 使用子测试区分测试迭代
分测验
例子:
from unittest import TestCase
param_list = [('a', 'a'), ('a', 'b'), ('b', 'b')]
class TestDemonstrateSubtest(TestCase):
def test_works_as_expected(self):
for p1, p2 in param_list:
with self.subTest():
self.assertEqual(p1, p2)
你也可以给subTest()指定一个自定义消息和参数值:
with self.subTest(msg="Checking if p1 equals p2", p1=p1, p2=p2):
用鼻子
鼻测试框架支持这一点。
示例(下面的代码是包含测试的文件的全部内容):
param_list = [('a', 'a'), ('a', 'b'), ('b', 'b')]
def test_generator():
for params in param_list:
yield check_em, params[0], params[1]
def check_em(a, b):
assert a == b
nosetests命令输出信息如下:
> nosetests -v
testgen.test_generator('a', 'a') ... ok
testgen.test_generator('a', 'b') ... FAIL
testgen.test_generator('b', 'b') ... ok
======================================================================
FAIL: testgen.test_generator('a', 'b')
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python2.5/site-packages/nose-0.10.1-py2.5.egg/nose/case.py", line 203, in runTest
self.test(*self.arg)
File "testgen.py", line 7, in check_em
assert a == b
AssertionError
----------------------------------------------------------------------
Ran 3 tests in 0.006s
FAILED (failures=1)
我使用元类和装饰器来生成测试。您可以检查我的实现python_wrap_cases。这个库不需要任何测试框架。
你的例子:
import unittest
from python_wrap_cases import wrap_case
@wrap_case
class TestSequence(unittest.TestCase):
@wrap_case("foo", "a", "a")
@wrap_case("bar", "a", "b")
@wrap_case("lee", "b", "b")
def testsample(self, name, a, b):
print "test", name
self.assertEqual(a, b)
控制台输出:
testsample_u'bar'_u'a'_u'b' (tests.example.test_stackoverflow.TestSequence) ... test bar
FAIL
testsample_u'foo'_u'a'_u'a' (tests.example.test_stackoverflow.TestSequence) ... test foo
ok
testsample_u'lee'_u'b'_u'b' (tests.example.test_stackoverflow.TestSequence) ... test lee
ok
你也可以使用发电机。例如,这段代码用参数a__list和b__list生成所有可能的测试组合
import unittest
from python_wrap_cases import wrap_case
@wrap_case
class TestSequence(unittest.TestCase):
@wrap_case(a__list=["a", "b"], b__list=["a", "b"])
def testsample(self, a, b):
self.assertEqual(a, b)
控制台输出:
testsample_a(u'a')_b(u'a') (tests.example.test_stackoverflow.TestSequence) ... ok
testsample_a(u'a')_b(u'b') (tests.example.test_stackoverflow.TestSequence) ... FAIL
testsample_a(u'b')_b(u'a') (tests.example.test_stackoverflow.TestSequence) ... FAIL
testsample_a(u'b')_b(u'b') (tests.example.test_stackoverflow.TestSequence) ... ok
元编程很有趣,但它也会碍事。这里的大多数解决方案都很难:
有选择地启动测试
指向给出测试名称的代码
所以,我的第一个建议是遵循简单/显式路径(适用于任何测试运行程序):
import unittest
class TestSequence(unittest.TestCase):
def _test_complex_property(self, a, b):
self.assertEqual(a,b)
def test_foo(self):
self._test_complex_property("a", "a")
def test_bar(self):
self._test_complex_property("a", "b")
def test_lee(self):
self._test_complex_property("b", "b")
if __name__ == '__main__':
unittest.main()
既然我们不应该重复,我的第二个建议建立在Javier的回答之上:接受基于属性的测试。假设库:
“在生成测试用例方面比我们人类更加无情地迂回”
会提供简单的计数例子吗
与任何测试运行程序一起工作
具有更多有趣的特性(统计数据、额外的测试输出……)
类TestSequence (unittest.TestCase):
st.text @given (st.text () ()
Def test_complex_property(self, a, b):
self.assertEqual (a, b)
为了测试您的特定示例,只需添加:
@example("a", "a")
@example("a", "b")
@example("b", "b")
为了只运行一个特定的示例,您可以注释掉其他示例(提供的示例将首先运行)。你可能想要使用@given(st.nothing())。另一种选择是将整个区块替换为:
@given(st.just("a"), st.just("b"))
好的,您没有不同的测试名称。但也许你只需要:
被测属性的描述性名称。
哪个输入会导致失败(伪造的例子)。
有趣的例子