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

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

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

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


person Gohu    schedule 01.07.2009    source источник


Ответы (16)


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

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

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

  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, чтобы указать «Быстро» или «Медленно». Вы можете создать подкласс 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)
person S.Lott    schedule 01.07.2009
comment
хорошо, если бы это было так просто на земле С ++ для стресс-тестов моего алгоритма :) - person Matt Joiner; 08.02.2010
comment
@MattJoiner: Я знаю друга, который использовал Python и ctypes для написания и запуска модульных тестов для кода C / C ++. Во всяком случае, это не по теме этого вопроса. - person Denilson Sá Maia; 05.08.2014
comment
Мне пришлось изменить код, чтобы это работало. Вот что у меня сработало: test_class = eval(sys.argv[1]) suite = unittest.TestLoader().loadTestsFromTestCase(test_class) unittest.TextTestRunner().run(suite) - person Dirty Penguin; 08.12.2015
comment
Небольшое изменение: я думаю, что это должен быть addTest, а не addTests. Документы говорят, что addTests предназначен для итерации тестов, а addTest предназначен для добавления класса TestCase. - person Sam Bobel; 30.09.2018

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

python -m unittest test_module.TestClass.test_method

Дополнительную информацию можно найти здесь.

person Amit Kotlovski    schedule 23.07.2013
comment
Я отлаживаю свои тестовые примеры, поэтому этот метод более полезен, чем принятый ответ. Спасибо. - person luanjunyi; 21.12.2014
comment
Это также работает с несколькими тестами одновременно. Просто убедитесь, что они разделены пробелами следующим образом: python -m unittest test_module.TestClass.test_method test_module.TestClass.test_method2. Так что даже если у вас есть несколько связанных тестовых примеров, которые нужно запустить, это все равно может быть действительно полезно. - person eestrada; 12.06.2015

Я делаю это с помощью простого 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 …
person Alfe    schedule 28.06.2017

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

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

person Tzury Bar Yochay    schedule 02.10.2012
comment
У меня работает в Python 2.7.11 и 3.5.1. Имена - это атрибуты, доступные в модуле. account = TestAccount не нужен, также можно использовать python test.py TestAccount. - person Rob W; 24.05.2016
comment
Чтобы расширить мой предыдущий комментарий (и констатировать очевидное): этот параметр командной строки работает при условии, что вызывается unittest.main(). Например. if __name__ == '__main__': unittest.main() - person Rob W; 24.05.2016

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

  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

person rob    schedule 01.07.2009
comment
Да, я знаю о фиктивных объектах, но думаю, что это будет слишком сложно. Python не подключается напрямую к кластеру, он выполняет серию сценариев bash, поведение которых мне нужно проверить. Итак, мне нужно было бы создать фиктивные сценарии, которые будут вести себя так же, как и настоящие, за исключением последней части подключения, но тогда мне придется поддерживать оба одновременно и убедиться, что они эквивалентны тому, что я хочу для тестирования ... Спасибо за ваш ответ о тестовых наборах, я выбрал ответ от С. Лотта, потому что он немного более подробный, но в основном это то же самое. - person Gohu; 01.07.2009

Поскольку вы используете 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
person Thomas Ahle    schedule 26.06.2014

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

На основе ярлыков

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

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

Реализация

Я реализовал это в двух частях:

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

Рабочая реализация заключается в следующем: https://gist.github.com/fragmuffin/a245f59c3bdcd51/a245f59bdcd51

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

В результате ...

$ ./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 тоже работает

person FraggaMuffin    schedule 11.01.2018

Или вы можете использовать функцию 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')
person joe    schedule 13.06.2013
comment
Вы можете использовать пропустить тестовое оформление, например. @ unittest2.skipUnless (runlowtests (), медленный тест) - person gaoithe; 22.03.2017

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

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
person devsnd    schedule 13.02.2019

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

Взгляните, например, на Nose.

person Lennart Regebro    schedule 01.07.2009
comment
Спасибо за ответ, но я думаю, что это немного перебор, поэтому я выбрал TestSuites. - person Gohu; 01.07.2009
comment
@GeorgeStocker: Вы не можете использовать Google, чтобы найти новый URL? - person Lennart Regebro; 13.04.2013
comment
@LennartRegebro Ваш ответ должен стоять отдельно, без ссылок, необходимых для его завершения. Ссылки должны быть дополнительной информацией. В его нынешнем виде ваш ответ не отвечает на вопрос. Не говоря уже о полезной части ответа 404s. См. Также: meta.stackexchange.com/questions/8231/ - person George Stocker; 13.04.2013
comment
@GeorgeStocker: он отвечает на вопрос полезным образом (а именно указывает, что если вам нужна такая функциональность, лучше всего использовать фреймворк, расширяющий unittest), ссылка является дополнительной информацией. Я исправил ссылку. - person Lennart Regebro; 13.04.2013

Я попробовал ответ S.Lott:

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)
person Dirty Penguin    schedule 08.12.2015

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

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

Дизайн: см. Ниже. Это сглаживает пространство имен, поэтому его можно выбирать по имени класса 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 )
person FDS    schedule 14.10.2015

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

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

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

python ./tests.py class_Name.test_30311

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

person Keith    schedule 14.02.2017

Я нашел этот ответ, пытаясь понять, как просто запускать определенные классы тестов; Например,

class TestCase1(unittest.TestCase):
    def some_test(self):
        self.assertEqual(True, True)

class TestCase2(unittest.TestCase):
    def some_other_test(self):
        self.assertEqual(False, False)

Мне нужен был быстрый способ закомментировать TestCase1 или TestCase2, который не требовал от меня выбора более 100 строк кода, и в конце концов я остановился на этом:

if __name__ == "__main__":
    tests = []
    tests.append("TestCase1")
    # tests.append("TestCase2")
    unittest.main(defaultTest=tests)

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

person Locane    schedule 12.01.2021

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

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

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

person Satrapes    schedule 12.05.2015

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

class TestStuff(unittest.TestCase):
    def test1():
    def test2():

Тогда я запускаю это ...

t = TestStuff()
t.test1()
t.test2()

(Я использую Spyder IDE для анализа данных, это может быть не идеально для IDE с инструментами тестирования Slicker)

person Selah    schedule 22.07.2021