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

StackOverflow https://stackoverflow.com/questions/1068246

Вопрос

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

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

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

Спасибо.

Это было полезно?

Решение

Значение по умолчанию 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])
    

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

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

  1. Сначала добавьте метки к тестам (с помощью пользовательского @testlabel класс декоратор)
  2. Обычай 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top