[Tutor] unittest for: Raises an exception

Ben Finney ben+python at benfinney.id.au
Wed Feb 18 21:40:55 CET 2015


Sydney Shall <s.shall at virginmedia.com> writes:

> My test code is the following:
>
> def test_func_getSurplusLabourTime_Exc(self):
>
> self.assertRaises(ValueError,self.cwp.getSurplusLabourTime(self.cwp.ww,self.cwp.uvc)) 
>
> [This last line should indented, but it refuses to do so!]

What is “it” which refuses to indent your text? You might need to use a
better message composition tool.

If you're typing into a Web application, please see the discussion
happening in this forum about appropriate email clients for posting
program code.

So I'll reformat that code for readability::

    def test_func_getSurplusLabourTime_Exc(self):
        self.assertRaises(
                ValueError,
                self.cwp.getSurplusLabourTime(self.cwp.ww, self.cwp.uvc))

> The traceback is as follows:
>
> ======================================================================
> ERROR: test_func_getSurplusLabourTime_Exc (__main__.Testcwp)
> ----------------------------------------------------------------------
> Traceback (most recent call last):
[…]
> "/Applications/Canopy.app/appdata/canopy-1.5.1.2730.macosx-x86_64/Canopy.app/Contents/lib/python2.7/unittest/case.py", line 475, in assertRaises
>     callableObj(*args, **kwargs)
> TypeError: 'float' object is not callable

The error message is correct: the ‘assertRaises’ method expects a
callable object in the second parameter, but you've supplied an object
which is not callable (a float).

Have a closer look at the documentation for ‘TestCase.assertRaises’::

    assertRaises(exception, callable, *args, **kwds)

    Test that an exception is raised when callable is called with any
    positional or keyword arguments that are also passed to assertRaises().

    <URL:https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertRaises>

It's unfortunate the documentation doesn't give an example. But note
that clause “when `callable` is called with any […] arguments that *are
also passed to assertRaises*”.

So you don't call the function yourself; if you do, you get back its
return value (in your case, a float object) and *that's* your argument
to ‘assertRaises’ — not the function you're trying to test!

Instead of calling the function and getting its return value, you pass
*the function itself* to ‘assertRaises’, along with any arguments you
want *the test method* to call it with.

This indirection is a little confusing, and again I'm unhappy the
documentation doesn't show an example. Here's one::

    def test_func_getSurplusLabourTime_Exc(self):
        self.assertRaises(
                ValueError,
                self.cwp.getSurplusLabourTime,
                self.cwp.ww, self.cwp.uvc)

What the documentation does show as an example, though, is a new-ish
feature that might suit you better.

You can now make your test code more comprehensible by instead using the
‘assertRaises’ method as a context manager, and then you just call your
function normally. Like this::

    def test_func_getSurplusLabourTime_Exc(self):
        with self.assertRaises(ValueError):
            self.cwp.getSurplusLabourTime(self.cwp.ww, self.cwp.uvc)

Context managers are a very helpful feature that can make code more
elegant and readable. They might seem a little magic for now if you
haven't learned about them yet, but this is a good demonstration of
the improvement they can make.

-- 
 \       “… one of the main causes of the fall of the Roman Empire was |
  `\        that, lacking zero, they had no way to indicate successful |
_o__)                  termination of their C programs.” —Robert Firth |
Ben Finney



More information about the Tutor mailing list