运行Python参数化测试方法时出错
开发工具: PyCharm社区版 3.1.1
Python版本: 2.7.6
我在使用DDT来进行测试参数化,具体可以参考这个链接:http://ddt.readthedocs.org/en/latest/example.html
我想在PyCharm中选择并运行测试类里的参数化测试方法,下面是一个例子:
from unittest import TestCase
from ddt import ddt, data
@ddt
class Test_parameterized(TestCase):
def test_print_value(self):
print 10
self.assertIsNotNone(10)
@data(10, 20, 30, 40)
def test_print_value_parametrized(self, value):
print value
self.assertIsNotNone(value)
当我在代码中找到第一个测试方法test_print_value
,然后按下ctrl+Shift+F10(或者从右键菜单中选择Run Unittest test_print...
选项),这个测试就会被执行。
但是当我尝试对参数化测试做同样的操作时,我遇到了错误:
Test framework quit unexpectedly
输出信息包含:
/usr/bin/python2 /home/s/App/pycharm-community-3.1.1/helpers/pycharm/utrunner.py
/home/s/Documents/Py/first/fib/test_parametrized.py::Test_parameterized::test_print_value_parametrized true
Testing started at 10:35 AM ...
Traceback (most recent call last):
File "/home/s/App/pycharm-community-3.1.1/helpers/pycharm/utrunner.py", line 148, in <module>
testLoader.makeTest(getattr(testCaseClass, a[2]), testCaseClass))
AttributeError: 'TestLoader' object has no attribute 'makeTest'
Process finished with exit code 1
不过,当我运行整个测试类(通过在代码中找到测试类名并使用前面提到的运行测试选项)时,所有的参数化测试和非参数化测试都会一起执行,没有出现错误。
问题在于如何独立运行测试类中的参数化方法。一个变通的方法是每个测试类只放一个参数化测试,但这样做显得有些乱。
2 个回答
当我遇到这个错误时,是因为我写的初始化函数是这样的:
def __init__(self):
super(ClassInheritingTestCase, self).__init__()
后来我把它改成了下面这样,结果就正常工作了:
def __init__(self, *args, **kwargs):
super(ClassInheritingTestCase, self).__init__(*args, **kwargs)
问题出在我没有正确传递 *args 和 **kwargs。
其实这是PyCharm中的一个问题,涉及到一个叫做utrunner.py的文件,它负责运行单元测试。如果你在使用DDT(数据驱动测试),会看到有两个装饰器,分别是@ddt和@data,它们的作用是为每一组数据创建独立的测试。在后台,这些测试会有不同的名字,比如:
@ddt
class MyTestClass(unittest.TestCase):
@data(1, 2)
def test_print(self, command):
print command
这会生成以下测试名称: - test_print_1_1 - test_print_2_2
当你尝试从类中运行一个测试(右键点击 -> 运行 'Unittest test_print')时,PyCharm会遇到问题,因为它在加载你的测试print_1_1和print_2_2时,实际上是试图加载test_print这个测试。
如果你查看utrunner.py的代码:
if a[1] == "":
# test function, not method
all.addTest(testLoader.makeTest(getattr(module, a[2])))
else:
testCaseClass = getattr(module, a[1])
try:
all.addTest(testCaseClass(a[2]))
except:
# class is not a testcase inheritor
all.addTest(
testLoader.makeTest(getattr(testCaseClass, a[2]), testCaseClass))
并进行调试,你就会发现这个问题。
好的,我的解决办法是从类中加载正确的测试。这只是一个临时解决方案,并不完美。不过,由于DDT会把测试用例作为类中的另一个方法添加进来,所以很难找到其他方法来识别正确的测试用例,只能通过字符串比较来实现。因此,替代:
try:
all.addTest(testCaseClass(a[2]))
你可以尝试使用:
try:
all_tests = testLoader.getTestCaseNames(getattr(module, a[1]))
for test in all_tests:
if test.startswith(a[2]):
if test.split(a[2])[1][1].isdigit():
all.addTest(testLoader.loadTestsFromName(test, getattr(module,a[1])))
检查主名称后面是否有数字是一个临时解决方案,可以用来排除类似的测试用例:
test_print
test_print_another_case
但当然,这样做并不能排除以下情况:
test_if_prints_1
test_if_prints_2
所以在最坏的情况下,如果我们没有好的命名规范,我们可能会运行类似的测试,但在大多数情况下,这个方法应该能正常工作。