如何正确使用单元测试的assertRaises()与NoneType对象

201 投票
4 回答
198258 浏览
提问于 2025-04-16 18:12

我做了一个简单的测试案例:

def setUp(self):

  self.testListNone = None

def testListSlicing(self):

  self.assertRaises(TypeError, self.testListNone[:1])

我本来希望这个测试能通过,但却出现了一个异常:

Traceback (most recent call last):

    self.assertRaises(TypeError, self.testListNone[:1])

TypeError: 'NoneType' object is unsubscriptable

我以为使用assertRaises会通过,因为会抛出TypeError这个异常。那这是什么原因呢?

4 个回答

109

通常使用 assertRaises 的方法是调用一个函数:

self.assertRaises(TypeError, test_function, args)

来测试函数调用 test_function(args) 是否会引发一个 TypeError 错误。

但是,self.testListNone[:1] 的问题在于,Python 会立即计算这个表达式,也就是说在调用 assertRaises 方法之前就已经执行了。这么做的原因是希望 test_functionargs 作为不同的参数传给 self.assertRaises,这样 assertRaises 就可以在 try...except 代码块中调用 test_function(args),从而捕获到异常。

因为你已经定义了 self.testListNone = None,而且你需要一个可以调用的函数,你可以像这样使用 operator.itemgetter

import operator
self.assertRaises(TypeError, operator.itemgetter, (self.testListNone,slice(None,1)))

因为

operator.itemgetter(self.testListNone,slice(None,1))

这其实是说 self.testListNone[:1] 的一种冗长方式,但它把函数(operator.itemgetter)和参数分开了。

181

这个问题是因为 TypeError 错误在 assertRaises 被调用之前就出现了,因为 assertRaises 的参数需要在调用方法之前先被计算出来。你需要传递一个 lambda 表达式,像这样:

self.assertRaises(TypeError, lambda: self.testListNone[:1])
300

如果你正在使用Python 2.7或更高版本,你可以利用assertRaises这个功能,它可以作为上下文管理器来使用,你可以这样做:

with self.assertRaises(TypeError):
    self.testListNone[:1]

如果你使用的是Python 2.6,除了之前提到的方法,还有另一种方式,就是使用unittest2。这个库是将unittest的新功能移植到Python 2.6上,你可以用上面的代码让它工作。

注意:我非常喜欢unittest的新功能(比如SkipTest、测试发现等),所以我打算尽可能多地使用unittest2。我建议你也这样做,因为在Python 2.6或更低版本中,unittest提供的功能远不如unittest2丰富。

撰写回答