在PyDev中初始化单元测试?

11 投票
4 回答
9251 浏览
提问于 2025-04-16 14:24

我在Eclipse中使用PyDev进行Python代码的单元测试。我右键点击相应的文件,选择 运行方式 -> Python单元测试。关于这个插件,我有几个问题:

  1. 有没有办法在这个类中的任何其他测试之前执行一个 setUpClass 方法?目前我只能让 setUp 方法工作,它是在这个类的每个测试之前被调用的。
  2. 有没有办法在执行任何测试之前进行全局初始化?类似于 setUpModule,但我也无法在使用PyDev单元测试时让它运行。

提前感谢任何回答和评论^^
Cherio Woltan

示例:

class TestClass(unittest.TestCase):

  @classmethod
  def setUpClass(self):
      print "Setup"    

  def test1(self):
      print "Test1"
  def test2(self):
      print "Test2"

如果我用 运行方式 -> Python单元测试 来运行这个,setUpClass 方法就不会被调用。

4 个回答

2

编辑:总结

通过调试器逐步检查你的测试案例后,看来这是PyDev的测试运行器的一个限制,它不支持setUpClass(),至少在我使用的1.6.5版本中是这样。

也许在PyDev的2.0版本中会修复这个问题,但在此之前,我认为我们得按照CarlS的建议,继续使用__init__()

详细信息

PyDev 1.6.5的PyDevTestSuite类使用了:

def run(self, result):
    for index, test in enumerate(self._tests):
        if result.shouldStop:
            break
        test(result)

        # Let the memory be released! 
        self._tests[index] = None

    return result

这与Python 2.6中的TestSuite.run()非常相似,而Python 2.7.1的unittest中的TestSuite.run()则做了更多的事情:

def run(self, result, debug=False):
    topLevel = False
    if getattr(result, '_testRunEntered', False) is False:
        result._testRunEntered = topLevel = True

    for test in self:
        if result.shouldStop:
            break

        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)):
                continue

        if not debug:
            test(result)
        else:
            test.debug()

    if topLevel:
        self._tearDownPreviousClass(None, result)
        self._handleModuleTearDown(result)
    return result

旧答案

我怀疑这可能与所使用的Python版本有关。

如果你查看窗口 > 偏好设置 > PyDev > 解释器 - Python,看看使用的是哪个Python解释器,你可能会发现它是2.7之前的版本,而setUpClass是在2.7中引入的,如果我没记错的话。

引用一个更新的Python版本,我猜你的测试就能正常工作了。

5

好的,我来试试这个:我使用的是Pydev,并且一直在探索如何用“nosetests”来启动测试,所以这和我有点关系。我的解决方案有点像是临时拼凑的,但在PyDev中选择“以单元测试调试”时似乎有效:

print "before any tests (global) just once..."
class MyTestCase(unittest.TestCase):
   class_setup_called = False
   def __init__(self, test_name):
      unittest.TestCase.__init__(self, test_name)
      if not self.__class__.class_setup_called:
          self.setUpClass()
          self.__class__.class_setup_called = True
   @staticmethod
   def setUpClass():
      print "before first test only..."
   def setUp(self):
      print "before each test..."

不过,这在使用nosetests时就不行了,但在Pydev中运行是可以的。我猜nosetests是设计成在每个测试方法运行之前先创建测试对象,这样的话你的初始化方法就可以用了。

我对nosetests的赞美之词真是说不完:

  • 支持异常处理和定时测试。
  • 支持注解“归属”。
  • 可以和性能分析、覆盖率以及xunit结果报告整合。
  • 可以递归发现和执行测试用例。

举个例子:

import unittest
@raises(TypeError)          
def test_forexceptions(self): 
    pass

@attr('benchmark')
@timed(5.0)
def test_benchmark(self):
    pass # do something real slow and run nosetests --with-profile -a benchmark

补充说明

进一步调查发现,如果你使用nosetests,它会为你提供支持,见“http://somethingaboutorange.com/mrl/projects/nose/1.0.0/writing_tests.html”。

你可以有:

包级别的清理:这些在包级别的初始化脚本中。

def setup_package()
def teardown_package()

模块级别的清理:

def setup_module()
def teardown_module()

类级别的:

class MyTestCase(unittest.TestCase):
    @classmethod
    def setup_class(cls): pass
    @classmethod
    def teardown_class(cls): pass

还有测试方法级别的:

class MyTestCase(unittest.TestCase):
    def setUp(self): pass
    def tearDown(cls): pass

我喜欢用“setup_”开头的名字,因为这样可以清楚地区分出nose特有的入口点。我已经验证过这些在命令行通过nosetests运行时效果很好。但在Pydev中选择“以单元测试运行...”时就不行了。一个可能的解决方案是写一个pydev插件,使用nose来运行测试……也许有人已经做过?你可以把我的临时拼凑和nose的结合起来,调用公共模块函数来完成实际工作。理想情况下,我们的init()应该能够识别出是从Pydev启动的。

7

这是一个PyDev的bug,已经在2.0.1版本中修复了。

setUpModule()tearDownModule()setUpClass()tearDownClass()这些函数在之前的PyDev 2.0.0版本及更早的版本中,在“Python单元测试”的运行配置下没有被执行。到了2.0.1版本,它们在“Python单元测试”和“Python运行”配置下都能正常运行。我自己也测试过,确认了这一点。

撰写回答