[翻译]  Can Python's unittest test in parallel, like nose can?

[CHINESE]  Python的单元测试可以像鼻子一样并行测试吗?


Python's NOSE testing framework has the concept of running multiple tests in parallel.

Python的NOSE测试框架具有并行运行多个测试的概念。

The purpose of this is not to test concurrency in the code, but to make tests for code that has "no side-effects, no ordering issues, and no external dependencies" run faster. The performance gain comes from concurrent I/O waits when they are accessing different devices, better use of multi CPUs/cores, and by running time.sleep() statements in parallel.

这样做的目的不是测试代码中的并发性,而是为没有副作用,没有排序问题,没有外部依赖性的代码进行测试,运行得更快。性能增益来自于访问不同设备时的并发I / O等待,更好地使用多CPU /内核以及并行运行time.sleep()语句。

I believe the same thing could be done with Python's unittest testing framework, by having a plugin Test Runner.

我相信通过使用插件测试运行器,可以使用Python的unittest测试框架完成同样的事情。

Has anyone had any experience with such a beast, and can they make any recommendations?

有没有人对这样的野兽有任何经验,他们可以提出任何建议吗?

7 个解决方案

#1


18  

Python unittest's builtin testrunner does not run tests in parallel. It probably wouldn't be too hard write one that did. I've written my own just to reformat the output and time each test. That took maybe 1/2 a day. I think you can swap out the TestSuite class that is used with a derived one that uses multiprocess without much trouble.

Python unittest的内置testrunner不会并行运行测试。写一个这样做可能不会太难。我自己写的只是为了重新格式化每个测试的输出和时间。那可能每天花费1/2。我认为你可以换掉与使用多进程的派生版本一起使用的TestSuite类,而不会有太多麻烦。

#2


15  

The testtools package is an extension of unittest which supports running tests concurrently. It can be used with your old test classes that inherit unittest.TestCase.

testtools包是unittest的扩展,它支持同时运行测试。它可以与继承unittest.TestCase的旧测试类一起使用。

For example:

例如:

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


2  

Please use pytest-xdist, if you want parallel run.

如果你想并行运行,请使用pytest-xdist。

The pytest-xdist plugin extends py.test with some unique test execution modes:

pytest-xdist插件使用一些独特的测试执行模式扩展了py.test:

  • test run parallelization: if you have multiple CPUs or hosts you can use those for a combined test run. This allows to speed up development or to use special resources of remote machines.
  • 测试运行并行化:如果您有多个CPU或主机,则可以将它们用于组合测试运行。这允许加速开发或使用远程机器的特殊资源。

[...]

[...]

More info: Rohan Dunham's blog

更多信息:Rohan Dunham的博客

#4


1  

If you only need Python3 suport, consider using my fastunit.

如果您只需要Python3支持,请考虑使用我的fastunit。

I just change few code of unittest, making test case run as coroutines.

我只更改了unittest的几个代码,使测试用例作为协同程序运行。

It really saved my time.

这真的节省了我的时间。

I just finished it last week, and may not testing enough, if any error happens, please let me know, so that I can make it better, thanks!

我上周刚刚完成它,可能测试不够,如果发生任何错误,请告诉我,以便我可以做得更好,谢谢!

#5


1  

Another option that might be easier, if you don't have that many test cases and they are not dependent, is to kick off each test case manually in a separate process.

如果您没有那么多测试用例并且它们不依赖,那么另一个可能更容易的选择是在单独的进程中手动启动每个测试用例。

For instance, open up a couple tmux sessions and then kick off a test case in each session using something like:

例如,打开几个tmux会话,然后在每个会话中使用以下内容启动测试用例:

python -m unittest -v MyTestModule.MyTestClass.test_n

#6


0  

If this is what you did initially

runner = unittest.TextTestRunner()
runner.run(suite)

-----------------------------------------

replace it with

from concurrencytest import ConcurrentTestSuite, fork_for_tests

concurrent_suite = ConcurrentTestSuite(suite, fork_for_tests(4))
runner.run(concurrent_suite)

#7


0  

You can override the unittest.TestSuite and implement some concurrency paradigm. Then, you use your customized TestSuite class just like normal unittest. In the following example, I implement my customized TestSuite class using async:

您可以覆盖unittest.TestSuite并实现一些并发范例。然后,您就像正常的单元测试一样使用自定义的TestSuite类。在以下示例中,我使用async实现自定义的TestSuite类:

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)

In the main, I just construct my customized TestSuite class CustomTestSuite, add all the test cases, and finally run it.

总的来说,我只是构建我自定义的TestSuite类CustomTestSuite,添加所有测试用例,最后运行它。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2018 ITdaan.com 粤ICP备14056181号