Jenkins中的Python单元测试?

159

如何使Jenkins执行Python的单元测试用例?是否可以从内置的unittest包中获得JUnit样式的XML输出?


1
所有的答案都假定您想从命令行启动测试用例。但是,如果您想以编程方式运行测试,请尝试以下代码:import nose ; nose.runmodule() # aka nose.run(defaultTest=__name__) - MarkHu
1
在我看来,简单的建议是使用“py.test --junitxml results.xml test.py”来回答这个问题。可以使用“yum install pytest”安装py.test。然后,您可以运行任何unittest python脚本并获取jUnit xml结果。 - gaoithe
2
@gaoithe 回答了 Jenkins 的部分,但没有满足使用内置的 unittest 模块的要求。在那个项目中,这是一个必备的要求。 - erikbstack
@erikb85 当我说“运行任何unittest python脚本”时,我指的是使用unittest模块的脚本。 - gaoithe
6个回答

194

示例测试:

tests.py:

# tests.py

import random
try:
    import unittest2 as unittest
except ImportError:
    import unittest

class SimpleTest(unittest.TestCase):
    @unittest.skip("demonstrating skipping")
    def test_skipped(self):
        self.fail("shouldn't happen")

    def test_pass(self):
        self.assertEqual(10, 7 + 3)

    def test_fail(self):
        self.assertEqual(11, 7 + 3)

使用 pytest 进行 JUnit 测试

运行测试的命令如下:

py.test --junitxml results.xml tests.py

results.xml:

<?xml version="1.0" encoding="utf-8"?>
<testsuite errors="0" failures="1" name="pytest" skips="1" tests="2" time="0.097">
    <testcase classname="tests.SimpleTest" name="test_fail" time="0.000301837921143">
        <failure message="test failure">self = &lt;tests.SimpleTest testMethod=test_fail&gt;

    def test_fail(self):
&gt;       self.assertEqual(11, 7 + 3)
E       AssertionError: 11 != 10

tests.py:16: AssertionError</failure>
    </testcase>
    <testcase classname="tests.SimpleTest" name="test_pass" time="0.000109910964966"/>
    <testcase classname="tests.SimpleTest" name="test_skipped" time="0.000164031982422">
        <skipped message="demonstrating skipping" type="pytest.skip">/home/damien/test-env/lib/python2.6/site-packages/_pytest/unittest.py:119: Skipped: demonstrating skipping</skipped>
    </testcase>
</testsuite>

JUnit with nose

使用以下命令运行测试:

nosetests --with-xunit

nosetests.xml:

<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="nosetests" tests="3" errors="0" failures="1" skip="1">
    <testcase classname="tests.SimpleTest" name="test_fail" time="0.000">
        <failure type="exceptions.AssertionError" message="11 != 10">
            <![CDATA[Traceback (most recent call last):
File "/opt/python-2.6.1/lib/python2.6/site-packages/unittest2-0.5.1-py2.6.egg/unittest2/case.py", line 340, in run
testMethod()
File "/home/damien/tests.py", line 16, in test_fail
self.assertEqual(11, 7 + 3)
File "/opt/python-2.6.1/lib/python2.6/site-packages/unittest2-0.5.1-py2.6.egg/unittest2/case.py", line 521, in assertEqual
assertion_func(first, second, msg=msg)
File "/opt/python-2.6.1/lib/python2.6/site-packages/unittest2-0.5.1-py2.6.egg/unittest2/case.py", line 514, in _baseAssertEqual
raise self.failureException(msg)
AssertionError: 11 != 10
]]>
        </failure>
    </testcase>
    <testcase classname="tests.SimpleTest" name="test_pass" time="0.000"></testcase>
    <testcase classname="tests.SimpleTest" name="test_skipped" time="0.000">
        <skipped type="nose.plugins.skip.SkipTest" message="demonstrating skipping">
            <![CDATA[SkipTest: demonstrating skipping
]]>
        </skipped>
    </testcase>
</testsuite>

使用nose2进行JUnit测试

您需要使用nose2.plugins.junitxml插件。您可以像通常一样使用配置文件配置nose2,或使用--plugin命令行选项配置。

运行测试:

nose2 --plugin nose2.plugins.junitxml --junit-xml tests

nose2-junit.xml:

<testsuite errors="0" failures="1" name="nose2-junit" skips="1" tests="3" time="0.001">
  <testcase classname="tests.SimpleTest" name="test_fail" time="0.000126">
    <failure message="test failure">Traceback (most recent call last):
  File "/Users/damien/Work/test2/tests.py", line 18, in test_fail
    self.assertEqual(11, 7 + 3)
AssertionError: 11 != 10
</failure>
  </testcase>
  <testcase classname="tests.SimpleTest" name="test_pass" time="0.000095" />
  <testcase classname="tests.SimpleTest" name="test_skipped" time="0.000058">
    <skipped />
  </testcase>
</testsuite>

使用unittest-xml-reporting的JUnit

将以下内容添加到tests.py文件中

if __name__ == '__main__':
    import xmlrunner
    unittest.main(testRunner=xmlrunner.XMLTestRunner(output='test-reports'))

使用以下命令运行测试:

python tests.py

test-reports/TEST-SimpleTest-20131001140629.xml:

<?xml version="1.0" ?>
<testsuite errors="1" failures="0" name="SimpleTest-20131001140629" tests="3" time="0.000">
    <testcase classname="SimpleTest" name="test_pass" time="0.000"/>
    <testcase classname="SimpleTest" name="test_fail" time="0.000">
        <error message="11 != 10" type="AssertionError">
<![CDATA[Traceback (most recent call last):
  File "tests.py", line 16, in test_fail
    self.assertEqual(11, 7 + 3)
AssertionError: 11 != 10
]]>     </error>
    </testcase>
    <testcase classname="SimpleTest" name="test_skipped" time="0.000">
        <skipped message="demonstrating skipping" type="skip"/>
    </testcase>
    <system-out>
<![CDATA[]]>    </system-out>
    <system-err>
<![CDATA[]]>    </system-err>
</testsuite>

4
对于简单的建议'py.test --junitxml results.xml test.py',请点赞(+1)。使用'yum install pytest'安装py.test。然后你就可以运行任何unittest的Python脚本并获取jUnit xml结果。 - gaoithe
2
如果你想使用 unittest-xml-reporting 并从 测试发现功能 中受益,你可以输入 unittest.main(module=None, testRunner=xmlrunner.XMLTestRunner(output='test-reports')) - Rosberg Linhares
@RosbergLinhares,为什么你需要使用module=None来进行测试发现?它的工作方式与答案中描述的完全相同:unittest.main(testRunner=xmlrunner.XMLTestRunner(output='test-reports')) - acm
@RosbergLinhares,在测试发现期间,模块仅被导入但不被执行。那么,这些解决方案中的任何一个都应该如何与发现一起工作呢?我刚刚尝试了一下,它们都没有起作用。或者是我漏掉了什么吗? - Konstantin
@Konstantin,我也不得不包括module=None才能正确运行。 - Brent Fisher
1
即使对我来说添加module=None也不起作用。 它仍然无法发现测试,我错过了什么吗? - Gaurav Parek

20

我推荐使用nose。现在已经内置了基本的XML报告功能,只需使用--with-xunit命令行选项即可生成一个nosetests.xml文件。例如:

nosetests --with-xunit

然后在构建后添加"发布JUnit测试结果报告"操作,并在"测试报告XMLs"字段中填写nosetests.xml(假设您在$WORKSPACE中运行了nosetests)。


12
你可以安装unittest-xml-reporting包,将生成XML的测试运行器添加到内置的unittest中。
我们使用pytest,它内置了XML输出(这是一个命令行选项)。
无论哪种方式,都可以通过运行shell命令来执行单元测试。

7
python -m pytest --junit-xml=pytest_unit.xml source_directory/test/unit || true # tests may fail

在Jenkins中作为shell运行此命令,您可以将报告作为pytest_unit.xml构件获取。


4

我使用了nosetests。有一些插件可以输出Jenkins所需的XML格式。


3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接