Python 中的单元测试理论?
Unit test Theories in Python?
前世我做了一些 Java 开发,发现 JUnit Theories 非常有用。 Python有没有类似的机制?
目前我正在做类似的事情:
def some_test(self):
cases = [('some sample input', 'some expected value'),
('some other input', 'some other expected value')]
for value, expected in cases:
result = method_under_test(value)
self.assertEqual(expected, result)
但这很笨拙,如果第一个"case"失败,其他所有失败都是运行。
我不知道在任何常见的测试框架中有任何内置函数。您的解决方案的唯一问题是迭代在测试内部。相反,它应该在外面并生成测试,可能是这样的
import unittest
def _apply(func, args):
"""Return a function with args applied after first argument"""
def wrapped(self):
return func(self, *args)
return wrapped
class TheoryMeta(type):
"""Metaclass that replaces test methods with multiple methods for each test case"""
def __new__(meta, name, bases, attrs):
newattrs = {}
cases = attrs.pop('cases', [])
for name, value in attrs.items():
if not name.startswith('test') or not callable(value):
newattrs[name] = value
continue
for n, args in enumerate(cases):
test_name = '%s_%d' % (name, n)
newattrs[test_name] = _apply(value, args)
return super().__new__(meta, name, bases, newattrs)
class TestCase(unittest.TestCase, metaclass=TheoryMeta):
pass
然后要使用它,创建一个 TestCase
子类,该子类具有 cases
属性,该属性是应用于测试用例中每个测试方法的参数列表。
class TestAdd(TestCase):
cases = [
# (a, b)
(1, 1),
(2, 0),
(3, 0),
]
def test_add(self, a, b):
self.assertEqual(a + b, 2)
======================================================================
FAIL: test_add_2 (__main__.__qualname__)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 7, in wrapped
return func(self, *args)
File "test.py", line 41, in test_add
self.assertEqual(a + b, 2)
AssertionError: 3 != 2
----------------------------------------------------------------------
Ran 3 tests in 0.001s
根据您的需要和测试设置,您可能会发现在 TestCase 上只使用猴子补丁生成的测试方法比使用元类更好。或者您可以在 TestLoader
上的覆盖 loadTestsFrom... 中生成它们。无论如何,使用示例数据生成测试方法。
看起来 pytest 可以做这样的事情:https://docs.pytest.org/en/latest/parametrize.html
我自己还没试过。
事实证明,built-in Python 3.4 -- subTest
: https://docs.python.org/3.4/library/unittest.html#distinguishing-test-iterations-using-subtests
不如 py.test 的参数化测试或 jUnit 理论那么优雅,但如果您想要 standard-lib 方法并且正在使用 Python 的相对较新版本,它是一个选项。
前世我做了一些 Java 开发,发现 JUnit Theories 非常有用。 Python有没有类似的机制?
目前我正在做类似的事情:
def some_test(self):
cases = [('some sample input', 'some expected value'),
('some other input', 'some other expected value')]
for value, expected in cases:
result = method_under_test(value)
self.assertEqual(expected, result)
但这很笨拙,如果第一个"case"失败,其他所有失败都是运行。
我不知道在任何常见的测试框架中有任何内置函数。您的解决方案的唯一问题是迭代在测试内部。相反,它应该在外面并生成测试,可能是这样的
import unittest
def _apply(func, args):
"""Return a function with args applied after first argument"""
def wrapped(self):
return func(self, *args)
return wrapped
class TheoryMeta(type):
"""Metaclass that replaces test methods with multiple methods for each test case"""
def __new__(meta, name, bases, attrs):
newattrs = {}
cases = attrs.pop('cases', [])
for name, value in attrs.items():
if not name.startswith('test') or not callable(value):
newattrs[name] = value
continue
for n, args in enumerate(cases):
test_name = '%s_%d' % (name, n)
newattrs[test_name] = _apply(value, args)
return super().__new__(meta, name, bases, newattrs)
class TestCase(unittest.TestCase, metaclass=TheoryMeta):
pass
然后要使用它,创建一个 TestCase
子类,该子类具有 cases
属性,该属性是应用于测试用例中每个测试方法的参数列表。
class TestAdd(TestCase):
cases = [
# (a, b)
(1, 1),
(2, 0),
(3, 0),
]
def test_add(self, a, b):
self.assertEqual(a + b, 2)
======================================================================
FAIL: test_add_2 (__main__.__qualname__)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 7, in wrapped
return func(self, *args)
File "test.py", line 41, in test_add
self.assertEqual(a + b, 2)
AssertionError: 3 != 2
----------------------------------------------------------------------
Ran 3 tests in 0.001s
根据您的需要和测试设置,您可能会发现在 TestCase 上只使用猴子补丁生成的测试方法比使用元类更好。或者您可以在 TestLoader
上的覆盖 loadTestsFrom... 中生成它们。无论如何,使用示例数据生成测试方法。
看起来 pytest 可以做这样的事情:https://docs.pytest.org/en/latest/parametrize.html
我自己还没试过。
事实证明,built-in Python 3.4 -- subTest
: https://docs.python.org/3.4/library/unittest.html#distinguishing-test-iterations-using-subtests
不如 py.test 的参数化测试或 jUnit 理论那么优雅,但如果您想要 standard-lib 方法并且正在使用 Python 的相对较新版本,它是一个选项。