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



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



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



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



спасибо.

724   13  

13 ответов:

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

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

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

  1. "быстрые" подмножество.

    fast = TestSuite()
    fast.addTests( TestFastThis )
    fast.addTests( TestFastThat )
    
  2. "медленные" подмножество.

    slow = TestSuite()
    slow.addTests( TestSlowAnother )
    slow.addTests( TestSlowSomeMore )
    
  3. "весь" набор.

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

обратите внимание, что я скорректировал имена TestCase, чтобы указать быстрый и медленный. Вы можете подкласс блок.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)

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

$ python -m unittest test_module.TestClass.test_method

дополнительная информация здесь

на самом деле, можно передать имена тестового случая как 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

чтобы проверить оба случая

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

  1. определить свой собственный набор тестов для класса
  2. создайте фиктивные классы кластерного соединения, которые будут возвращать фактические данные.

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

вернуться к варианту (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

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

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

SLOW_TESTS=1 python -m unittest …

так как вы используете 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

или вы можете использовать

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

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

Я попытался ответить @Слотт это:

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)

я нашел другое решение, основанное на том, как unittest.skip декоратор работает. Установив __unittest_skip__ и __unittest_skip_why__.

на основе этикеток

я хотел применить систему маркировки, чтобы обозначить некоторые тесты как quick,slow,glacier,memoryhog,cpuhog,core и так далее.

затем запустите all 'quick' tests или run everything except 'memoryhog' tests, ваш основной белый список / черный список настройки

реализация

я реализовал это в 2 частях:

  1. Сначала добавьте метки в тесты (через пользовательский @testlabel класс декоратора)
  2. Custom unittest.TestRunner чтобы определить, какие тесты пропустить, и изменить содержимое списка тестов перед выполнением.

рабочая реализация находится в этом gist: https://gist.github.com/fragmuffin/a245f59bdcd457936c3b51aa2ebb3f6c

(полностью рабочий пример был слишком долго, чтобы поставить здесь)

результат бытие...

$ ./runtests.py --blacklist foo
test_foo (test_things.MyTest2) ... ok
test_bar (test_things.MyTest3) ... ok
test_one (test_things.MyTests1) ... skipped 'label exclusion'
test_two (test_things.MyTests1) ... skipped 'label exclusion'

----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK (skipped=2)

все MyTests1 тесты класса пропускаются, потому что он имеет foo метки.

--whitelist работает

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

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

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

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

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

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

дизайн: видите ниже. Это плющит пространство имен так можно выбрать по имени класса 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 )

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

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

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

python ./tests.py имя_класса.test_30311

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

наслаждайтесь, Кит

Comments

    Ничего не найдено.