在PyDev中初始化单元测试?
我在Eclipse中使用PyDev进行Python代码的单元测试。我右键点击相应的文件,选择 运行方式 -> Python单元测试。关于这个插件,我有几个问题:
- 有没有办法在这个类中的任何其他测试之前执行一个 setUpClass 方法?目前我只能让 setUp 方法工作,它是在这个类的每个测试之前被调用的。
- 有没有办法在执行任何测试之前进行全局初始化?类似于 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 个回答
编辑:总结
通过调试器逐步检查你的测试案例后,看来这是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版本,我猜你的测试就能正常工作了。
好的,我来试试这个:我使用的是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启动的。
这是一个PyDev的bug,已经在2.0.1版本中修复了。
setUpModule()
、tearDownModule()
、setUpClass()
和tearDownClass()
这些函数在之前的PyDev 2.0.0版本及更早的版本中,在“Python单元测试”的运行配置下没有被执行。到了2.0.1版本,它们在“Python单元测试”和“Python运行”配置下都能正常运行。我自己也测试过,确认了这一点。