Тест на единицу Python:как запустить только часть тестового файла?
-
21-08-2019 - |
Вопрос
У меня есть тестовый файл, который содержит тесты, занимающие довольно много времени (они отправляют вычисления в кластер и ждут результата).Все это находится в определенном классе TestCase.
Поскольку они требуют времени и, кроме того, вряд ли сломаются, я бы хотел иметь возможность выбирать, выполняется ли это подмножество тестов или нет (лучшим способом было бы использовать аргумент командной строки, т. е. "./tests.py --offline
"или что-то в этом роде), так что я мог бы проводить большинство тестов часто и быстро, а весь набор - время от времени, когда у меня есть время.
На данный момент я просто использую unittest.main()
чтобы начать тесты.
Спасибо.
Решение
Значение по умолчанию 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])
Обратите внимание, что я скорректировал имена тестовых наборов, чтобы они указывали на Fast vs.Медленно.Вы можете создать подкласс unittest.TestLoader для анализа названий классов и создания нескольких загрузчиков.
Затем ваша основная программа может анализировать аргументы командной строки с помощью опциональный анализ или 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
протестировать оба случая
Я делаю это с помощью простого 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 …
В принципе, у вас есть два способа сделать это:
- Определите свой собственный набор тестов для класса
- Создайте фиктивные классы кластерного соединения, которые будут возвращать фактические данные.
Я убежденный сторонник второго подхода;модульный тест следует тестируйте только очень маленький фрагмент кода, а не сложные системы (такие как базы данных или кластеры).Но я понимаю, что это не всегда возможно;иногда создание макетов обходится просто слишком дорого, или цель тестирования действительно заключается в сложной системе.
Возвращаясь к варианту (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
Поскольку вы используете 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
Или вы можете воспользоваться 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')
Я нашел другое решение, основанное на том, как unittest.skip
работает декоратор.Установив __unittest_skip__
и __unittest_skip_why__
.
Основанный на ярлыках
Я хотел применить систему маркировки, чтобы обозначить некоторые тесты следующим образом quick
, slow
, glacier
, memoryhog
, cpuhog
, core
, и так далее.
Тогда беги all 'quick' tests
, или run everything except 'memoryhog' tests
, ваша базовая настройка белого списка / blacklist
Реализация
Я реализовал это в 2 частях:
- Сначала добавьте метки к тестам (с помощью пользовательского
@testlabel
класс декоратор) - Обычай
unittest.TestRunner
чтобы определить, какие тесты следует пропустить, и изменить содержимое списка тестов перед выполнением.
Рабочая реализация заключается в следующем: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
label.
--whitelist
также работает
Рассмотрите возможность использования специального тестировщика, такого как py.test, nose или, возможно, даже zope.testing.Все они имеют параметры командной строки для выбора тестов.
Посмотрите, например, на Нос: https://pypi.python.org/pypi/nose/1.3.0
Я попробовал ответить @slott's answer:
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)
Я нашел другой способ выбрать методы test_ *, которые я хочу запускать, только добавив к ним атрибут.По сути, вы используете метакласс для оформления вызываемых объектов внутри класса TestCase, которые имеют атрибут StepDebug, с помощью декоратора unittest.skip.Более подробная информация о
Пропуск всех модульных тестов, кроме одного на 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
Удаление имени класса и названия теста запускает все тесты в вашем файле.Я нахожу, что с этим ГОРАЗДО проще иметь дело, чем со встроенным методом, поскольку я на самом деле не меняю свою команду в CLI.Просто добавьте параметр.
Наслаждайся, Кит
Я создал декоратор, который позволяет помечать тесты как медленные и пропускать их с помощью переменной окружения
from unittest import skip
import os
def slow_test(func):
return skipIf('SKIP_SLOW_TESTS' in os.environ, 'Skipping slow test')(func)
Теперь вы можете пометить свои тесты как медленные следующим образом:
@slow_test
def test_my_funky_thing():
perform_test()
И пропустите медленные тесты, установив SKIP_SLOW_TESTS
переменная среды:
SKIP_SLOW_TESTS=1 python -m unittest