Jeśli naprawdę chcesz mieć wiele unitestów, potrzebujesz wielu metod. Jedynym sposobem, aby to osiągnąć, jest generowanie kodu. Możesz to zrobić za pomocą metaclasses lub przez ulepszenie klasy po definicji, w tym (jeśli używasz Pythona 2.6) przez dekorator klas.
Oto rozwiązanie, które wyszukuje członków "multitest" i "multitest_values" i używa ich do budowania metod testowych w locie. Nie elegancki, ale robi mniej więcej to, co chcesz:
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()
To jest wyjście od kiedy go uruchomić
% 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)
można natychmiast zobaczyć niektóre z problemów, które występują z generowania kodu. Skąd pochodzi "test_gt_1"? Mógłbym zmienić nazwę na dłuższy "test_multitest_gt_1", ale który test to 1? Lepiej byłoby zacząć od _0 zamiast _1, i być może w twoim przypadku wiesz, że wartości mogą być używane jako nazwa funkcji Pythona.
Nie podoba mi się to podejście. Pracowałem nad podstawami kodu, które autogenerowały metody testowe (w jednym przypadku przy użyciu metaklasu) i odkryłem, że było to trudniejsze do zrozumienia, niż było użyteczne. Kiedy test się nie powiódł, trudno było ustalić źródło niepowodzenia i trudno było trzymać się debugowania kodu, aby zbadać przyczynę niepowodzenia.
(Usuwanie błędów w przykładzie, który tu napisałem, nie jest tak trudne, jak to podejście z metaklasem, z którym miałem pracować.)
Znaleźliście sposób to zrobić? A może znalazłeś inne narzędzie do tego zadania? Potrzebuję takiego zachowania. – legesh
http://thebongtraveller.blogspot.sg/2015/12/art-of-unittestwriting-auto-generation.html Czy to to samo? –