В Python unittest.Порядок выполнения тестового случая
есть ли способ в Python unittest
чтобы задать порядок выполнения тестовых случаев?
в своем TestCase
класс, некоторые тестовые случаи имеют побочные эффекты, которые устанавливают условия для правильной работы других. Теперь я понимаю, что правильный способ сделать это-использовать setUp()
чтобы сделать все настройки realted вещи, но я хотел бы реализовать дизайн, где каждый последующий тест строит немного больше состояния, которое может использовать следующий. Я нахожу это гораздо более элегантно.
class MyTest(TestCase):
def test_setup(self):
#do something
def test_thing(self)
#do something that depends on test_setup()
в идеале, Я хотел бы, чтобы тесты выполнялись в том порядке, в котором они появляются в классе. Похоже, что они работают в алфавитном порядке.
7 ответов:
не делайте их независимыми тестами - если вы хотите монолитный тест, напишите монолитный тест.
class Monolithic(TestCase): def step1(self): ... def step2(self): ... def _steps(self): for name in sorted(dir(self)): if name.startswith("step"): yield name, getattr(self, name) def test_steps(self): for name, step in self._steps(): try: step() except Exception as e: self.fail("{} failed ({}: {})".format(step, type(e), e))
Если тест позже начинает отказывать, и вы хотите получить информацию обо всех неудачных шагах вместо остановки тестового набора на первом неудачном шаге, вы можете использовать
subtests
особенность: https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests(функция субтеста доступна через
unittest2
для версии до Python 3.4:https://pypi.python.org/pypi/unittest2)
его хорошая практика всегда писать монолитный тест для таких ожиданий, однако если вы тупой чувак, как я, то вы можете просто написать уродливые методы в алфавитном порядке, чтобы они были отсортированы от a до b, как указано в документах pythonhttp://docs.python.org/library/unittest.html
обратите внимание, что порядок, в котором будут выполняться различные тестовые случаи определяется путем сортировки имен тестовых функций по отношению к встроенный заказ для строк
пример:
def test_a_first(): print "1" def test_b_next(): print "2" def test_c_last(): print "3"
http://docs.python.org/library/unittest.html
обратите внимание, что порядок, в котором будут выполняться различные тестовые случаи, определяется сортировкой имен тестовых функций по встроенному порядку для строк.
Так что
test_setup
'имя S имеет наименьшее значение.обратите внимание, что вы не должны полагаться на такое поведение - различные тестовые функции должны быть независимы от порядка о казни. посмотреть ngcohlanответ выше для решения, если вам явно нужен заказ.
старый вопрос, но другой способ, который я не видел в списке связанных вопросов:использовать
TestSuite
.другой способ выполнить заказ-добавить тесты в
unitest.TestSuite
. Это, кажется, уважает порядок, в котором тесты добавляются в набор с помощьюsuite.addTest(...)
. Для этого:
создайте один или несколько подклассов TestCase,
class FooTestCase(unittest.TestCase): def test_ten(): print('Testing ten (10)...') def test_eleven(): print('Testing eleven (11)...') class BarTestCase(unittest.TestCase): def test_twelve(): print('Testing twelve (12)...') def test_nine(): print('Testing nine (09)...')
создать вызываемый набор тестов поколения добавлено в нужном порядке, адаптированный документы и этот вопрос:
def suite(): suite = unittest.TestSuite() suite.addTest(BarTestCase('test_nine')) suite.addTest(FooTestCase('test_ten')) suite.addTest(FooTestCase('test_eleven')) suite.addTest(BarTestCase('test_twelve')) return suite
выполните набор тестов, например,
if __name__ == '__main__': runner = unittest.TextTestRunner(failfast=True) runner.run(suite())
для контекста, у меня была потребность в этом и не был удовлетворен другими вариантами. Я остановился на вышеуказанном способе выполнения заказа теста. Я не видел, чтобы этот метод TestSuite перечислял какие-либо из нескольких "вопросов упорядочения юнит-тестов" (например, это вопрос и другие, в том числе порядок выполнения или изменение порядка или тесты ордер).
тесты, которые очень зависят друг от друга должны быть явно включены в один тест.
тесты, которые требуют различных уровней настройки, также могут иметь свои соответствующие
setUp()
работает достаточно настройки-различные способы мыслимые.иначе
unittest
ручки тестовые классы и методы испытаний внутри тестовых классов в алфавитном порядке по умолчанию (даже еслиloader.sortTestMethodsUsing
нет).dir()
используется внутри, который сортирует по гарантии.последняя поведение может быть использовано для практичность - например, для того, чтобы сначала запустить последние рабочие тесты, чтобы ускорить цикл редактирования-testrun. Но это поведение не должно использоваться для установления реальных зависимостей. Учтите, что тесты могут выполняться индивидуально с помощью параметров командной строки и т. д.
@ncoghlan ответ был именно то, что я искал, когда я пришел к этой теме. Я закончил тем, что изменил его, чтобы позволить запускать каждый шаг-тест, даже если предыдущий шаг уже вызвал ошибку; это помогает мне (и, возможно, Вам!) для обнаружения и планирования распространения ошибок в многопоточном программном обеспечении, ориентированном на базу данных.
class Monolithic(TestCase): def step1_testName1(self): ... def step2_testName2(self): ... def steps(self): ''' Generates the step methods from their parent object ''' for name in sorted(dir(self)): if name.startswith('step'): yield name, getattr(self, name) def test_steps(self): ''' Run the individual steps associated with this test ''' # Create a flag that determines whether to raise an error at # the end of the test failed = False # An empty string that the will accumulate error messages for # each failing step fail_message = '' for name, step in self.steps(): try: step() except Exception as e: # A step has failed, the test should continue through # the remaining steps, but eventually fail failed = True # get the name of the method -- so the fail message is # nicer to read :) name = name.split('_')[1] # append this step's exception to the fail message fail_message += "\n\nFAIL: {}\n {} failed ({}: {})".format(name, step, type(e), e) # check if any of the steps failed if failed is True: # fail the test with the accumulated exception message self.fail(fail_message)
Я закончил с простым решением, которое сработало для меня:
class SequentialTestLoader(unittest.TestLoader): def getTestCaseNames(self, testCaseClass): test_names = super().getTestCaseNames(testCaseClass) testcase_methods = list(testCaseClass.__dict__.keys()) test_names.sort(key=testcase_methods.index) return test_names
а то
unittest.main(testLoader=utils.SequentialTestLoader())