Coder Perfect

What is the best way to ensure that a Python function throws an exception?

Problem

How do you construct a unittest that only fails if a function fails to throw the anticipated exception?

Asked by Daryl Spitzer

Solution #1

Use the unittest module’s TestCase.assertRaises (or TestCase.failUnlessRaises) for example:

import mymod

class MyTestCase(unittest.TestCase):
    def test1(self):
        self.assertRaises(SomeCoolException, mymod.myfunc)

Answered by Moe

Solution #2

Since Python 2.7, you may obtain a hold of the actual Exception object thrown by using context manager:

import unittest

def broken_function():
    raise Exception('This is broken')

class MyTestCase(unittest.TestCase):
    def test(self):
        with self.assertRaises(Exception) as context:
            broken_function()

        self.assertTrue('This is broken' in context.exception)

if __name__ == '__main__':
    unittest.main()

http://docs.python.org/dev/library/unittest.html#unittest.TestCase.assertRaises

If you don’t surround context.exception with str in Python 3.5, you’ll get a TypeError.

self.assertTrue('This is broken' in str(context.exception))

Answered by Art

Solution #3

My prior answer’s code can be simplified to:

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction)

If a function accepts arguments, simply send them into assertRaises as follows:

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction, arg1, arg2)

Answered by Daryl Spitzer

Solution #4

Make use of your own resources. As a context manager, assertRaises:

    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'

In a Python shell, the best practice technique is quite simple to show.

The unittest library

Python 2.7 or Python 3:

import unittest

You may install a backport of Python 2.7’s unittest library, called unittest2, in Python 2.6 and just alias it as unittest:

import unittest2 as unittest

Now copy and paste the following type-safety test into your Python shell:

class MyTestCase(unittest.TestCase):
    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'
    def test_2_cannot_add_int_and_str(self):
        import operator
        self.assertRaises(TypeError, operator.add, 1, '1')

AssertRaises is used as a context manager in Test 1, ensuring that the problem is caught and cleaned up appropriately while being recorded.

Test two shows how we could write it without the context manager. The error type you plan to raise is the first parameter, the second argument is the function you’re testing, and the remaining args and keyword args are sent to that function.

I believe that using the context manager is significantly more easy, readable, and manageable.

To conduct the tests, follow these steps:

unittest.main(exit=False)

You’ll probably need the following in Python 2.6:

unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

And the following should appear on your terminal:

..
----------------------------------------------------------------------
Ran 2 tests in 0.007s

OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>

Attempting to add a 1 and a ‘1’ results in a TypeError, as we would anticipate.

For more verbose output, try this:

unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

Answered by Aaron Hall

Solution #5

This pattern should be followed in your code (this is a unittest module type test):

def test_afunction_throws_exception(self):
    try:
        afunction()
    except ExpectedException:
        pass
    except Exception:
       self.fail('unexpected exception raised')
    else:
       self.fail('ExpectedException not raised')

On Python < 2.7 this construct is useful for checking for specific values in the expected exception. The unittest function assertRaises only checks if an exception was raised.

Answered by Daryl Spitzer

Post is based on https://stackoverflow.com/questions/129507/how-do-you-test-that-a-python-function-throws-an-exception