Если вы действительно хотите использовать несколько unitttest, вам понадобится несколько методов. Единственный способ добиться этого - создать своего рода код. Вы можете сделать это с помощью метаклассов или путем настройки класса после определения, в том числе (если вы используете Python 2.6) с помощью декоратора класса.
Вот решение, которое ищет специальные члены multitest и multitest_values и использует их для создания методов тестирования на лету. Не элегантно, но делает примерно то, что вы хотите:
import unittest
import inspect
class SomeValue(object):
def __eq__(self, other):
return other in [1, 3, 4]
class ExampleTestCase(unittest.TestCase):
somevalue = SomeValue()
multitest_values = [1, 2, 3, 4]
def multitest(self, n):
self.assertEqual(self.somevalue, n)
multitest_gt_values = "ABCDEF"
def multitest_gt(self, c):
self.assertTrue(c > "B", c)
def add_test_cases(cls):
values = {}
functions = {}
# Find all the 'multitest*' functions and
# matching list of test values.
for key, value in inspect.getmembers(cls):
if key.startswith("multitest"):
if key.endswith("_values"):
values[key[:-7]] = value
else:
functions[key] = value
# Put them together to make a list of new test functions.
# One test function for each value
for key in functions:
if key in values:
function = functions[key]
for i, value in enumerate(values[key]):
def test_function(self, function=function, value=value):
function(self, value)
name ="test%s_%d" % (key[9:], i+1)
test_function.__name__ = name
setattr(cls, name, test_function)
add_test_cases(ExampleTestCase)
if __name__ == "__main__":
unittest.main()
Это результат, когда я его запустил
% python stackoverflow.py
.F..FF....
======================================================================
FAIL: test_2 (__main__.ExampleTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "stackoverflow.py", line 34, in test_function
function(self, value)
File "stackoverflow.py", line 13, in multitest
self.assertEqual(self.somevalue, n)
AssertionError: <__main__.SomeValue object at 0xd9870> != 2
======================================================================
FAIL: test_gt_1 (__main__.ExampleTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "stackoverflow.py", line 34, in test_function
function(self, value)
File "stackoverflow.py", line 17, in multitest_gt
self.assertTrue(c > "B", c)
AssertionError: A
======================================================================
FAIL: test_gt_2 (__main__.ExampleTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "stackoverflow.py", line 34, in test_function
function(self, value)
File "stackoverflow.py", line 17, in multitest_gt
self.assertTrue(c > "B", c)
AssertionError: B
----------------------------------------------------------------------
Ran 10 tests in 0.001s
FAILED (failures=3)
Вы можете сразу увидеть некоторые проблемы, возникающие при генерации кода. Откуда взялось "test_gt_1"? Я мог бы изменить имя на более длинное «test_multitest_gt_1», но тогда какой тест равен 1? Лучше было бы начать с _0 вместо _1, и, возможно, в вашем случае вы знаете, что значения могут использоваться как имя функции Python.
Мне такой подход не нравится. Я работал над базами кода, которые автоматически генерировали методы тестирования (в одном случае с использованием метакласса), и обнаружил, что это было намного сложнее понять, чем было полезно. Когда тест не удался, было трудно определить источник сбоя, и было трудно придерживаться кода отладки, чтобы выяснить причину сбоя.
(Отладка ошибок в примере, который я здесь написал, не так сложна, как тот конкретный подход к метаклассу, с которым мне пришлось работать.)
person
Andrew Dalke
schedule
05.11.2009