Python的NOSE测试框架具有running multiple tests in parallel的概念。
这样做的目的不是测试代码中的并发性,而是为没有副作用,没有排序问题,没有外部依赖性的代码进行测试,运行得更快。性能增益来自于访问不同设备时的并发I / O等待,更好地使用多CPU /内核以及并行运行time.sleep()语句。
我相信使用Python的单元测试框架可以通过插件Test Runner完成同样的事情。
有没有人有这种野兽的经验,他们可以提出任何建议吗?
答案 0 :(得分:21)
Python unittest的内置testrunner不会并行运行测试。写一个这样做可能不会太难。我自己写的只是为了重新格式化每个测试的输出和时间。那可能每天花费1/2。我认为你可以换掉与使用多进程的派生类一起使用的TestSuite类,而不会有太多麻烦。
答案 1 :(得分:19)
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())
答案 2 :(得分:6)
如果您想要并行运行,请使用pytest-xdist。
pytest-xdist插件使用一些独特的测试执行模式扩展了py.test:
- 测试运行并行化:如果您有多个CPU或主机,则可以将它们用于组合测试运行。这样可以加快开发速度或使用远程机器的特殊资源。
[...]
更多信息:Rohan Dunham's blog
答案 3 :(得分:3)
如果您没有那么多测试用例并且它们不依赖,那么另一个可能更容易的选择是在一个单独的过程中手动启动每个测试用例。
例如,打开几个tmux会话,然后在每个会话中使用类似以下的命令启动测试用例:
python -m unittest -v MyTestModule.MyTestClass.test_n
答案 4 :(得分:2)
如果您只需要Python3支持,请考虑使用我的fastunit。
我只需更改一些unittest代码,将测试用例作为协同程序运行。
这真的节省了我的时间。
我上周刚刚完成它,可能测试不够,如果发生任何错误,请告诉我,以便我能做得更好,谢谢!
答案 5 :(得分:1)
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)
答案 6 :(得分:-1)
您可以覆盖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
,添加了所有测试用例,最后运行它。