Python unittest: как запустить только часть тестового файла?

У меня есть тестовый файл, содержащий тесты, занимающие довольно много времени (они отправляют вычисления в кластер и ждут результата). Все они относятся к определенному классу TestCase.

Поскольку они требуют времени и, кроме того, вряд ли будут разбиваться, я хочу, чтобы иметь возможность выбрать, выполняется ли это подмножество тестов или не выполняется (лучший способ был бы с аргументом командной строки, то есть "./tests.py --offline" или что-то в этом роде), поэтому я мог запускать большую часть тестов часто и быстро, и весь набор время от времени, когда у меня есть время.

В настоящее время я просто использую unittest.main() для запуска тестов.

Спасибо.

Ответ 1

По умолчанию unittest.main() использует тестовый загрузчик по умолчанию, чтобы вывести TestSuite из модуля, в котором работает main.

Вам не нужно использовать это поведение по умолчанию.

Вы можете, например, сделать три экземпляра unittest.TestSuite.

  • "Быстрое" подмножество.

    fast = TestSuite()
    fast.addTests( TestFastThis )
    fast.addTests( TestFastThat )
    
  • Подменю "slow".

    slow = TestSuite()
    slow.addTests( TestSlowAnother )
    slow.addTests( TestSlowSomeMore )
    
  • "Целый" набор.

    alltests = unittest.TestSuite([fast, slow])
    

Обратите внимание, что я скорректировал имена TestCase, чтобы указать Fast vs. Slow. Вы можете подклассы unittest.TestLoader для анализа имен классов и создания нескольких загрузчиков.

Затем ваша основная программа может анализировать аргументы командной строки с помощью optparse или argparse (доступно с 2.7 или 3.2), чтобы выбрать, какой пакет вы хотите запустить, быстро, медленно или все.

Или вы можете доверять, что sys.argv[1] является одним из трех значений и использует что-то простое, чем это

if __name__ == "__main__":
    suite = eval(sys.argv[1])  # Be careful with this line!
    unittest.TextTestRunner().run(suite)

Ответ 2

Чтобы запустить только один конкретный тест, вы можете использовать:

$ python -m unittest test_module.TestClass.test_method

Подробнее здесь

Ответ 3

Собственно, имена тестового примера можно передать как sys.argv, и будут проверены только те случаи.

Например, предположим, что у вас

class TestAccount(unittest.TestCase):
    ...

class TestCustomer(unittest.TestCase):
    ...

class TestShipping(unittest.TestCase):
    ...

account = TestAccount
customer = TestCustomer
shipping = TestShipping

Вы можете позвонить

python test.py account

чтобы иметь только тесты учетной записи или даже

$ python test.py account customer

для проверки обоих случаев

Ответ 4

У вас есть в основном два способа сделать это:

  • Определите свой собственный набор тестов для класса
  • Создайте классные классы соединения кластера, которые возвратят фактические данные.

Я - сильный сторонник его второго подхода; a unit test должен тестировать только очень единицу кода, а не сложные системы (например, базы данных или кластеры). Но я понимаю, что это не всегда возможно; иногда создание макетов просто слишком дорого, или цель теста действительно находится в сложной системе.

Возвращаясь к опции (1), вы можете действовать следующим образом:

suite = unittest.TestSuite()
suite.addTest(MyUnitTestClass('quickRunningTest'))
suite.addTest(MyUnitTestClass('otherTest'))

а затем передать пакет тестируемому бегуну:

unittest.TextTestRunner().run(suite)

Дополнительная информация о документации python: http://docs.python.org/library/unittest.html#testsuite-objects

Ответ 5

Поскольку вы используете unittest.main(), вы можете просто запустить python tests.py --help, чтобы получить документацию:

Usage: tests.py [options] [test] [...]

Options:
  -h, --help       Show this message
  -v, --verbose    Verbose output
  -q, --quiet      Minimal output
  -f, --failfast   Stop on first failure
  -c, --catch      Catch control-C and display results
  -b, --buffer     Buffer stdout and stderr during test runs

Examples:
  tests.py                               - run default set of tests
  tests.py MyTestSuite                   - run suite 'MyTestSuite'
  tests.py MyTestCase.testSomething      - run MyTestCase.testSomething
  tests.py MyTestCase                    - run all 'test*' test methods
                                               in MyTestCase

То есть, вы можете просто сделать

python tests.py TestClass.test_method

Ответ 6

Я делаю это, используя простой skipIf:

import os

SLOW_TESTS = int(os.getenv('SLOW_TESTS', '0'))

@unittest.skipIf(not SLOW_TESTS, "slow")
class CheckMyFeature(unittest.TestCase):
    def runTest(self):
        …

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

Если я хочу выполнить его, несмотря на медленность, я просто вызываю свой script следующим образом:

SLOW_TESTS=1 python -m unittest …

Ответ 7

Или вы можете использовать функцию unittest.SkipTest(). Например, добавьте метод skipOrRunTest в ваш тестовый класс следующим образом:

def skipOrRunTest(self,testType):
    #testsToRun = 'ALL'
    #testsToRun = 'testType1, testType2, testType3, testType4,...etc'
    #testsToRun = 'testType1'
    #testsToRun = 'testType2'
    #testsToRun = 'testType3'
    testsToRun = 'testType4'              
    if ((testsToRun == 'ALL') or (testType in testsToRun)):
        return True 
    else:
        print "SKIPPED TEST because:\n\t testSuite '" + testType  + "' NOT IN testsToRun['" + testsToRun + "']" 
        self.skipTest("skipppy!!!")

Затем добавьте вызов этого метода skipOrRunTest к самому началу каждого из ваших модульных тестов следующим образом:

def testType4(self):
    self.skipOrRunTest('testType4')

Ответ 8

Посмотрите на использование выделенного testrunner, например py.test, носа или, возможно, даже zope.testing. Все они имеют параметры командной строки для выбора тестов.

Посмотрите, например, как нос: https://pypi.python.org/pypi/nose/1.3.0

Ответ 9

Я попробовал ответить @slott:

if __name__ == "__main__":
    suite = eval(sys.argv[1])  # Be careful with this line!
    unittest.TextTestRunner().run(suite)

Но это дало мне следующую ошибку:

Traceback (most recent call last):
  File "functional_tests.py", line 178, in <module>
    unittest.TextTestRunner().run(suite)
  File "/usr/lib/python2.7/unittest/runner.py", line 151, in run
    test(result)
  File "/usr/lib/python2.7/unittest/case.py", line 188, in __init__
    testMethod = getattr(self, methodName)
TypeError: getattr(): attribute name must be string

Следующие работали для меня:

if __name__ == "__main__":
    test_class = eval(sys.argv[1])
    suite = unittest.TestLoader().loadTestsFromTestCase(test_class)
    unittest.TextTestRunner().run(suite)

Ответ 10

Я нашел другой способ выбрать методы test_ *, которые я только хочу запустить, добавив к ним атрибут. В основном вы используете метакласс для украшения вызываемых объектов внутри класса TestCase, у которых есть атрибут StepDebug с помощью декодера unittest.skip. Дополнительная информация о

Пропуск всех модульных тестов, но один в Python с помощью декораторов и метаклассов

Я не знаю, является ли это лучшим решением, чем выше. Я просто предоставляю его как вариант.

Ответ 11

Не удалось найти хороший способ сделать это раньше, поэтому поделитесь здесь.

Цель: собрать набор тестовых файлов, чтобы они могли работать как единое целое, но мы все равно можем выбрать любой из них для самостоятельной работы.

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

Дизайн: см. ниже. Это выравнивает пространство имен, поэтому его можно выбрать по имени класса TestCase и оставить префикс "tests1.test_core":

./run-tests TestCore.test_fmap

код

  test_module_names = [
    'tests1.test_core',
    'tests2.test_other',
    'tests3.test_foo',
    ]

  loader = unittest.defaultTestLoader
  if args:
    alltests = unittest.TestSuite()
    for a in args:
      for m in test_module_names:
        try:
          alltests.addTest( loader.loadTestsFromName( m+'.'+a ) )
        except AttributeError as e:
          continue
  else:
    alltests = loader.loadTestsFromNames( test_module_names )

  runner = unittest.TextTestRunner( verbosity = opt.verbose )
  runner.run( alltests )

Ответ 12

Это единственное, что сработало для меня.

if __name__ == '__main__':
unittest.main( argv=sys.argv, testRunner = unittest.TextTestRunner(verbosity=2))

Когда я это вызвал, мне пришлось передать имя класса и тестового имени. Немного неудобно, так как у меня нет запоминания имени класса и теста.

python./tests.py class_Name.test_30311

Удаление имени класса и имени теста выполняет все тесты в вашем файле. Я считаю, что это намного проще справиться с встроенным методом, поскольку я не изменяю свою команду в CLI. Просто добавьте параметр.

Enjoy, Кит