196

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Andriusa
  • 2,224
  • 2
  • 17
  • 16
  • https://ongspxm.github.io/blog/2016/11/assertraises-testing-for-errors-in-unittest/ You can refer to this document as he/she has explained why use a lambda/context manager to wrap the code – Sanjeev Siva Apr 21 '20 at 11:33

4 Answers4

292

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
mouad
  • 67,571
  • 18
  • 114
  • 106
176

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])
Bas Bossink
  • 9,388
  • 4
  • 41
  • 53
109

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.

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • 3
    For a single assertRaises, this looks like the best answer. For a large bank of exception based tests (assuming they all have the same exception type) the with self.assertRaises(...) would be a good choice. – user632657 May 01 '14 at 17:55
  • 2
    Yeah, this should be the correct answer since it actually explains the problem. – Henry Gomersall Oct 03 '14 at 14:59
  • 3
    This is definitely the right answer, which explains the problem. – Ismael Dec 11 '14 at 13:31
  • Can you please help me in a related question ? https://stackoverflow.com/questions/39909935/how-do-you-show-an-error-message-when-a-test-does-not-throw-an-expected-exceptio – Erran Morad Oct 07 '16 at 17:11
  • +1 It can be simplified to `self.assertRaises(TypeError, self.testListNone.__getitem__, slice(None,1))`, because the operator.itemgetter adds some small useless complexity. – hynekcer Apr 06 '21 at 12:31
35

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))
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
saaj
  • 23,253
  • 3
  • 104
  • 105