How to properly use unit-testing's assertRaises() with NoneType objects

Question:

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 the test to pass, but I am getting an 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. What is the explanation?

Asked By: Andriusa

||

Answers:

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.

Answered By: unutbu

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])
Answered By: Bas Bossink

If you are using Python 2.7 or above, you can use the ability of assertRaises to be used as a context manager and do:

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

If you are using Python 2.6, another way beside the one given until now is to use unittest2 which is a back port of unittest‘s new features to Python 2.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, etc.) 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 Python 2.6 or lower.

Answered By: mouad

A complete snippet would look like the following. It expands mouad’s answer to asserting on the error’s message (or generally the string representation of its arguments), 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))
Answered By: saaj
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.