unittest: assertRaises() with an instance instead of a type

Steven D'Aprano steve+comp.lang.python at pearwood.info
Thu Mar 29 22:53:47 EDT 2012


On Thu, 29 Mar 2012 08:35:16 -0700, Ethan Furman wrote:

> Steven D'Aprano wrote:
>> On Wed, 28 Mar 2012 14:28:08 +0200, Ulrich Eckhardt wrote:
>> 
>>> Hi!
>>>
>>> I'm currently writing some tests for the error handling of some code.
>>> In this scenario, I must make sure that both the correct exception is
>>> raised and that the contained error code is correct:
>>>
>>>
>>>    try:
>>>        foo()
>>>        self.fail('exception not raised')
>>>    catch MyException as e:
>>>        self.assertEqual(e.errorcode, SOME_FOO_ERROR)
>>>    catch Exception:
>>>        self.fail('unexpected exception raised')
>> 
>> Secondly, that is not the right way to do this unit test. You are
>> testing two distinct things, so you should write it as two separate
>> tests:
> 
> I have to disagree -- I do not see the advantage of writing a second
> test that *will* fail if the first test fails as opposed to bundling
> both tests together, and having one failure.

Using that reasoning, your test suite should contain *one* ginormous test 
containing everything:

def testDoesMyApplicationWorkPerfectly(self):
    # TEST ALL THE THINGS!!!
    ...


since *any* failure in any part will cause cascading failures in every 
other part of the software which relies on that part. If you have a tree 
of dependencies, a failure in the root of the tree will cause everything 
to fail, and so by your reasoning, everything should be in a single test.

I do not agree with that reasoning, even when the tree consists of two 
items: an exception and an exception attribute.

The problem of cascading test failures is a real one. But I don't believe 
that the solution is to combine multiple conceptual tests into a single 
test. In this case, the code being tested covers two different concepts:

1. foo() will raise MyException. Hence one test for this.

2. When foo() raises MyException, the exception instance will include an
   errorcode attribute with a certain value. This is conceptually 
   separate from #1 above, even though it depends on it. 

Why is it conceptually separate? Because there may be cases where the 
caller cares about foo() raising MyException, but doesn't care about the 
errorcode. Hence errorcode is dependent but separate, and hence a 
separate test.


-- 
Steven



More information about the Python-list mailing list