运行Python参数化测试方法时出错

8 投票
2 回答
6160 浏览
提问于 2025-04-18 06:05

开发工具: 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 个回答

2

当我遇到这个错误时,是因为我写的初始化函数是这样的:

def __init__(self):
    super(ClassInheritingTestCase, self).__init__()

后来我把它改成了下面这样,结果就正常工作了:

def __init__(self, *args, **kwargs):
    super(ClassInheritingTestCase, self).__init__(*args, **kwargs)

问题出在我没有正确传递 *args 和 **kwargs。

6

其实这是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

所以在最坏的情况下,如果我们没有好的命名规范,我们可能会运行类似的测试,但在大多数情况下,这个方法应该能正常工作。

撰写回答