Может ли Python unittest тестировать параллельно, как нос?
Платформа тестирования Python NOSE предполагает параллельное выполнение нескольких тестов.
Цель этого состоит не в том, чтобы проверить параллельность в коде, а в том, чтобы тесты для кода, у которого "нет побочных эффектов, нет проблем с упорядочением и нет внешних зависимостей", выполнялись быстрее. Повышение производительности достигается за счет одновременного ожидания ввода-вывода, когда они обращаются к различным устройствам, лучшего использования нескольких процессоров/ядер и параллельного выполнения операторов time.sleep().
Я полагаю, что то же самое можно сделать с помощью инфраструктуры тестирования юнитов Python с помощью плагина Test Runner.
Кто-нибудь имел опыт работы с таким зверем, и могут ли они дать какие-либо рекомендации?
Ответы
Ответ 1
Python unittest встроенный testrunner не запускает тесты параллельно. Вероятно, это было бы не слишком сложно написать то, что было сделано. Я написал свой собственный, чтобы переформатировать выход и время каждого теста. Это заняло, может быть, 1/2 в день. Я думаю, вы можете поменять класс TestSuite, который используется с производным, который использует многопроцессор без особых проблем.
Ответ 2
Пакет testtools - это расширение unittest, которое поддерживает одновременное выполнение тестов. Его можно использовать со старыми тестовыми классами, которые наследуют unittest.TestCase
.
Например:
import unittest
import testtools
class MyTester(unittest.TestCase):
# Tests...
suite = unittest.TestLoader().loadTestsFromTestCase(MyTester)
concurrent_suite = testtools.ConcurrentStreamTestSuite(lambda: ((case, None) for case in suite))
concurrent_suite.run(testtools.StreamResult())
Ответ 3
Пожалуйста, используйте pytest-xdist, если вы хотите параллельный запуск.
Плагин pytest-xdist расширяет py.test некоторыми уникальными режимами выполнения теста:
- распараллеливание тестового прогона: если у вас несколько процессоров или хостов, вы можете использовать их для комбинированного тестового прогона. Это позволяет ускорить разработку или использовать специальные ресурсы удаленных машин.
[...]
Больше информации: блог Рохана Данхэма
Ответ 4
Если вам нужна только поддержка Python3, подумайте об использовании моего fastunit.
Я просто изменяю несколько кода unittest, делая тестовый сценарий выполненным как сопрограммы.
Это действительно спасло мое время.
Я только что закончил его на прошлой неделе и, возможно, недостаточно тестирую, если произошла какая-либо ошибка, сообщите мне, чтобы я мог сделать это лучше, спасибо!
Ответ 5
Другой вариант, который может быть проще, если у вас не так много тестовых примеров, и они не зависят от них, - это запустить каждый тестовый пример вручную в отдельном процессе.
Например, откройте пару сеансов tmux, а затем начните тестовый пример в каждом сеансе, используя что-то вроде:
python -m unittest -v MyTestModule.MyTestClass.test_n
Ответ 6
Если это то, что вы сделали первоначально
runner = unittest.TextTestRunner()
runner.run(suite)
-----------------------------------------
замените его на
from concurrencytest import ConcurrentTestSuite, fork_for_tests
concurrent_suite = ConcurrentTestSuite(suite, fork_for_tests(4))
runner.run(concurrent_suite)
Ответ 7
Вы можете переопределить unittest.TestSuite
и реализовать парадигму параллелизма. Затем вы используете настроенный TestSuite
класс так же, как нормальный unittest
. В следующем примере я реализую свой настраиваемый класс TestSuite
с помощью async
:
import unittest
import asyncio
class CustomTestSuite(unittest.TestSuite):
def run(self, result, debug=False):
"""
We override the 'run' routine to support the execution of unittest in parallel
:param result:
:param debug:
:return:
"""
topLevel = False
if getattr(result, '_testRunEntered', False) is False:
result._testRunEntered = topLevel = True
asyncMethod = []
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
for index, test in enumerate(self):
asyncMethod.append(self.startRunCase(index, test, result))
if asyncMethod:
loop.run_until_complete(asyncio.wait(asyncMethod))
loop.close()
if topLevel:
self._tearDownPreviousClass(None, result)
self._handleModuleTearDown(result)
result._testRunEntered = False
return result
async def startRunCase(self, index, test, result):
def _isnotsuite(test):
"A crude way to tell apart testcases and suites with duck-typing"
try:
iter(test)
except TypeError:
return True
return False
loop = asyncio.get_event_loop()
if result.shouldStop:
return False
if _isnotsuite(test):
self._tearDownPreviousClass(test, result)
self._handleModuleFixture(test, result)
self._handleClassSetUp(test, result)
result._previousTestClass = test.__class__
if (getattr(test.__class__, '_classSetupFailed', False) or
getattr(result, '_moduleSetUpFailed', False)):
return True
await loop.run_in_executor(None, test, result)
if self._cleanup:
self._removeTestAtIndex(index)
class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
# check that s.split fails when the separator is not a string
with self.assertRaises(TypeError):
s.split(2)
if __name__ == '__main__':
suite = CustomTestSuite()
suite.addTest(TestStringMethods('test_upper'))
suite.addTest(TestStringMethods('test_isupper'))
suite.addTest(TestStringMethods('test_split'))
unittest.TextTestRunner(verbosity=2).run(suite)
В main
, я просто построить свой индивидуальный TestSuite
класс CustomTestSuite
, добавьте все тестовые случаи, и, наконец, запустить его.