В Python unittest: как запустить только часть тестового файла?
у меня есть тестовый файл, который содержит тесты, занимающие довольно много времени (они отправляют вычисления в кластер и ждут результата). Все они находятся в определенном классе TestCase.
поскольку они занимают время и, кроме того, вряд ли сломаются, я хотел бы иметь возможность выбирать, выполняется ли это подмножество тестов или нет (лучший способ-с аргументом командной строки, т. е. "./tests.py --offline " или что-то в этом роде), так что я мог бы запустить большинство тестов часто и быстро и всего один раз в то время, когда у меня есть время.
на данный момент, я просто использую unittest.main() для начала тестов.
спасибо.
13 ответов:
по умолчанию
unittest.main()использует загрузчик тестов по умолчанию, чтобы сделать TestSuite из модуля, в котором работает main.вы не должны использовать это поведение по умолчанию.
вы можете, например, сделать три unittest.Тестов экземпляров.
"быстрые" подмножество.
fast = TestSuite() fast.addTests( TestFastThis ) fast.addTests( TestFastThat )"медленные" подмножество.
slow = TestSuite() slow.addTests( TestSlowAnother ) slow.addTests( TestSlowSomeMore )"весь" набор.
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), Вы можете продолжить таким образом:
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 частях:
- Сначала добавьте метки в тесты (через пользовательский
@testlabelкласс декоратора)- 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