Подтвердить что ты не робот

Порядок выполнения Python unittest.TestCase

Есть ли способ в Python unittest установить порядок выполнения тестовых примеров?

В моем текущем классе TestCase некоторые тестовые окна имеют побочные эффекты, которые задают условия для правильной работы остальных. Теперь я понимаю, что правильный способ сделать это состоит в том, чтобы использовать setUp() для выполнения всех заданий, связанных с реализацией, но я хотел бы реализовать проект, в котором каждый последующий тест строит немного больше состояний, которые может использовать следующий. Я нахожу это намного более элегантным.

class MyTest(TestCase):
  def test_setup(self):
   #do something
  def test_thing(self)
   #do something that depends on test_setup()

В идеале я бы хотел, чтобы тесты выполнялись в том порядке, в котором они появляются в классе. Похоже, что они работают в алфавитном порядке.

4b9b3361

Ответ 1

Не делайте их независимыми испытаниями - если вы хотите монолитный тест, напишите монолитный тест.

class Monolithic(TestCase):
  def step1(self):
      ...

  def step2(self):
      ...

  def _steps(self):
    for name in dir(self): # dir() result is implicitly sorted
      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 # отличительных-тест-итерации, использующие-подтесты

(Функция unittest2 доступна через unittest2 для версий до Python 3.4: https://pypi.python.org/pypi/unittest2)

Ответ 2

Хорошая практика всегда писать монолитный тест для таких ожиданий, однако, если у вас тупой чувак, как я, то вы можете просто писать уродливые поисковые методы в алфавитном порядке, чтобы они сортировались от a до b, как указано в python docs http://docs.python.org/library/unittest.html

Обратите внимание, что порядок, в котором будут выполняться различные тестовые примеры, определяется путем сортировки имен тестовых функций по отношению к встроенный порядок строк

Пример:

  def test_a_first():
  print "1"
  def test_b_next(): 
  print "2" 
  def test_c_last(): 
  print "3"

Ответ 3

http://docs.python.org/library/unittest.html

Обратите внимание, что порядок, в котором будут выполняться различные тестовые примеры, определяется путем сортировки имен тестовых функций по отношению к встроенному упорядочению для строк.

Поэтому просто убедитесь, что имя test_setup имеет наименьшее строковое значение.

Обратите внимание, что вы не должны полагаться на это поведение - разные функции тестирования должны быть независимы от порядка выполнения. См. ngcohlan ответьте выше для решения, если вам явно нужен заказ.

Ответ 4

Старый вопрос, но другой способ, который я не видел, указан в любых связанных вопросах: используйте 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 перечислял любой из нескольких "вопросов упорядочивания модульных тестов" (например, этот вопрос и другие, включая порядок выполнения или изменение порядка или порядок тестирования).

Ответ 5

Тесты, которые действительно зависят друг от друга, должны быть явно привязаны к одному тесту.

Тесты, требующие разных уровней настройки, также могут иметь соответствующий setUp() режим работы, достаточный для установки - различные способы мыслимы.

В противном случае unittest обрабатывает тестовые классы и методы тестирования внутри классов тестов по алфавиту по умолчанию (даже если loader.sortTestMethodsUsing - None). dir() используется внутри, который сортируется по гарантии.

Последнее поведение может быть использовано для практичности - например. для того, чтобы выполнить самые последние рабочие тесты, чтобы ускорить цикл edit-testrun. Но это поведение не должно использоваться для установления реальных зависимостей. Учтите, что тесты можно запускать индивидуально с помощью параметров командной строки и т.д.

Ответ 6

Ответ на

@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 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)

Ответ 7

Я закончил с простым решением, которое сработало для меня:

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())