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


75

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

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

Наразі я просто використовую unittest.main()для початку тестів.

Дякую.

Відповіді:


52

За замовчуванням 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)

добре, тепер, якби це було так просто в c ++ land для моїх стрес-тестів з алгоритму :)
Matt Joiner

@MattJoiner: Я знаю друга, який використовував Python і ctypesписав та запускав модульні тести проти коду C / C ++. У будь-якому випадку, це не стосується цього питання.
Denilson Sá Maia

1
Мені довелося змінити код, щоб це працювало. Ось, що мені test_class = eval(sys.argv[1]) suite = unittest.TestLoader().loadTestsFromTestCase(test_class) unittest.TextTestRunner().run(suite)
вдалося

1
Невелика зміна: я думаю, що це має бути addTest, а не addTests. Документи кажуть, що addTests призначений для повторних тестів, тоді як addTest - для додавання класу TestCase
Сем Бобел

85

Для запуску лише одного конкретного тесту ви можете використовувати:

$ python -m unittest test_module.TestClass.test_method

Більше інформації тут


1
Я налагоджую свої тестові приклади, тому цей метод набагато корисніший, ніж прийнята відповідь. Дякую.
luanjunyi

Це також працює з кількома тестами одночасно. Просто переконайтеся , що вони є пробілами так: python -m unittest test_module.TestClass.test_method test_module.TestClass.test_method2. Тож навіть якщо у вас є кілька пов’язаних тестових випадків, це все одно може бути дуже корисним.
eestrada

15

Я роблю це за допомогою простого 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 …

12

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

перевірити обидва випадки


2
Працює для мене в Python 2.7.11 та 3.5.1. Імена - це атрибути, доступні в модулі. account = TestAccountне потрібно, ви також можете використовувати python test.py TestAccount.
Роб W

Щоб продовжити мій попередній коментар (і зазначивши очевидне): Цей параметр командного рядка працює за умови, що unittest.main()його викликають. Наприкладif __name__ == '__main__': unittest.main()
Роб W

10

У вас є два способи зробити це:

  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


Так, я знаю про фальшиві предмети, але я думаю, що це буде занадто складно. Python не підключається безпосередньо до кластера, він проходить через ряд скриптів bash, поведінку яких мені потрібно перевірити. Отже, мені потрібно було б створити "макетні сценарії", які б поводились так само, як і справжні, за винятком тієї останньої частини підключення, але тоді мені довелося б підтримувати обидва одночасно і переконатися, що вони еквівалентні тому, що Я хочу протестувати ... Дякую за вашу відповідь щодо тестових наборів, я вибрав відповідь від С. Лотта, оскільки вона трохи детальніша, але в основному це одне і те ж.
Гоху

7

Оскільки ви використовуєте, 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

4

Я знайшов інше рішення, засноване на тому, як 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. Спеціально, 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ярлик.

--whitelist також працює


2

Або ви можете скористатися цією 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')

Ви можете використовувати пропуск тестової обробки, наприклад @ unittest2.skipUnless (runslowtests (), "повільний тест")
gaoithe

1

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

Шукайте, наприклад, Ніс: https://pypi.python.org/pypi/nose/1.3.0


Дякую за вашу відповідь, але я думаю, що це трохи надмірно, тому я вибрав TestSuites.
Гоху

1
@GeorgeStocker: Ви не можете використовувати Google для пошуку нової URL-адреси?
Леннарт Регебро

4
@LennartRegebro Ваша відповідь повинна стояти окремо без посилань, необхідних для її заповнення. Посилання повинні бути додатковою інформацією. У суті, ваша відповідь не відповідає на питання. Не кажучи вже про корисну частину відповіді 404s. Дивіться також: meta.stackexchange.com/questions/8231/…
Джордж Стокер

1
@GeorgeStocker: Він корисно відповідає на запитання (а саме вказує, що якщо ви бажаєте такого роду функціональних можливостей, найкращим способом є використання фреймворку, який розширює unittest), посилання є додатковою інформацією. Я зафіксував посилання.
Леннарт Регебро

1

Я спробував відповідь @ slott:

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)

0

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

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

Я не знаю, чи є це рішення кращим за наведені вище, я просто пропоную це як варіант.


0

Раніше не знайшов хорошого способу зробити це, тому діліться тут.

Мета: Зберіть разом набір тестових файлів, щоб їх можна було запускати як одиницю, але ми все одно можемо вибрати будь-який з них для запуску самостійно.

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

Дизайн: див. Нижче. Це згладжує простір імен, тому його можна вибрати за назвою класу 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 )

0

Це єдине, що мені вдалося.

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

Коли я зателефонував, хоча мені довелося передати ім'я класу та ім'я тесту. Трохи незручно, оскільки я не запам’ятав комбінацію назв класу та тесту.

python ./tests.py class_Name.test_30311

Видалення імені класу та імені тесту запускає всі тести у вашому файлі. Мені набагато легше мати справу з вбудованим методом, оскільки я насправді не змінюю свою команду на CLI. Просто додайте параметр.

Насолоджуйся, Кіт


0

Я створив декоратор, який дозволяє позначати тести як повільні тести та пропускати їх за допомогою змінної середовища

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.