How to properly use unit-testing's assertRaises() with NoneType objects?
PythonUnit TestingPython Problem Overview
I did a simple test case:
def setUp(self):
self.testListNone = None
def testListSlicing(self):
self.assertRaises(TypeError, self.testListNone[:1])
and I am expecting test to pass, but I am getting exception:
Traceback (most recent call last):
self.assertRaises(TypeError, self.testListNone[:1])
TypeError: 'NoneType' object is unsubscriptable
I thought that assertRaises will pass since TypeError exception will be raised?
Python Solutions
Solution 1 - Python
If you are using python2.7 or above you can use the ability of assertRaises to be use as a context manager and do:
with self.assertRaises(TypeError):
self.testListNone[:1]
If you are using python2.6 another way beside the one given until now is to use unittest2 which is a back port of unittest new feature to python2.6, and you can make it work using the code above.
N.B: I'm a big fan of the new feature (SkipTest, test discovery ...) of unittest so I intend to use unittest2 as much as I can. I advise to do the same because there is a lot more than what unittest come with in python2.6 <.
Solution 2 - Python
The problem is the TypeError
gets raised 'before' assertRaises
gets called since the arguments to assertRaises
need to be evaluated before the method can be called. You need to pass a lambda
expression like:
self.assertRaises(TypeError, lambda: self.testListNone[:1])
Solution 3 - Python
The usual way to use assertRaises
is to call a function:
self.assertRaises(TypeError, test_function, args)
to test that the function call test_function(args) raises a TypeError.
The problem with self.testListNone[:1]
is that Python evaluates the expression immediately, before the assertRaises
method is called. The whole reason why test_function
and args
is passed as separate arguments to self.assertRaises
is to allow assertRaises
to call test_function(args)
from within a try...except
block, allowing assertRaises
to catch the exception.
Since you've defined self.testListNone = None
, and you need a function to call, you might use operator.itemgetter like this:
import operator
self.assertRaises(TypeError, operator.itemgetter, (self.testListNone,slice(None,1)))
since
operator.itemgetter(self.testListNone,slice(None,1))
is a long-winded way of saying self.testListNone[:1]
, but which separates the function (operator.itemgetter
) from the arguments.
Solution 4 - Python
Complete snippet would look like the following. It expands @mouad's answer to asserting on error's message (or generally str
representation of its args
), which may be useful.
from unittest import TestCase
class TestNoneTypeError(TestCase):
def setUp(self):
self.testListNone = None
def testListSlicing(self):
with self.assertRaises(TypeError) as ctx:
self.testListNone[:1]
self.assertEqual("'NoneType' object is not subscriptable", str(ctx.exception))