python unittest assertRaises throws exception when assertRaises fails

15,051

Solution 1

The code as posted is wrong. For a start, class myClass(): shoudl be class myClass:. Also if name == "main": should be:

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

Apart from these problems, this fails because getName() is raising exception myExcOne and your test expects exception myExcTwo.

Here is some code that works. Please edit the code in your question so that it is easy for us to cut and paste it into a Python session:

import unittest

class myExcOne(Exception): "exception one"

class myExcTwo(Exception): "exception two"

class myClass:
    def getName(self):
        raise myExcTwo

class test_myClass(unittest.TestCase):
    def setUp(self):
        self.myClass = myClass()
    def testgetNameEmpty(self):
        #self.assertRaises(myExcOne,self.myClass.getName)
        self.assertRaises(myExcTwo,self.myClass.getName)

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

Solution 2

Starting with an aside, the () after the classname in a class statement is perfectly correct in modern Python -- not an error at all.

On the meat of the issue, assertRaises(MyException, foo) is documented to propagate exceptions raised by calling foo() whose type is NOT a subclass of MyException -- it only catches MyException and subclasses thereof. As your code raises an exception of one type and your test expects one of a different unrelated type, the raised exception will then propagate, as per the docs of the unittest module, here, and I quote:

The test passes if exception is raised, is an error if another exception is raised, or fails if no exception is raised.

And "is an error" means "propagates the other exception".

As you catch the exception propagating in your try/except block, you nullify the error, and there's nothing left for unittest to diagnose. If your purpose is to turn this error into a failure (a debatable strategy...), your except block should call self.fail.

Share:
15,051
Admin
Author by

Admin

Updated on June 14, 2022

Comments

  • Admin
    Admin almost 2 years

    I've got code where assertRaises throws an exception when assertRaises fails. I thought that if assertRaises fails then the test would fail and I'd get a report at the end that says the test failed. I wasn't expecting the exception to be thrown. Below is my code. I'm I doing something wrong? I'm using Python 2.6.2.

    import unittest
    
    class myClass:
    
        def getName(self):
    
            raise myExcOne, "my exception one"
            #raise myExcTwo, "my exception two"
            #return "a"
    
    class myExcOne(Exception):
        "exception one"
    
    class myExcTwo(Exception):
        "exception two"
    
    
    class test_myClass(unittest.TestCase):
    
        def setUp(self):
    
            self.myClass = myClass()
    
        def testgetNameEmpty(self):
            #self.assertRaises(myExcOne,self.myClass.getName)
            #self.assertRaises(myExcTwo,self.myClass.getName)
    
            try:
                self.assertRaises(myExcTwo,self.myClass.getName)
            except Exception as e:
                pass
    
    if __name__ == "__main__":
    
        #unittest.main()
    
        suite = unittest.TestLoader().loadTestsFromTestCase(test_myClass)
        unittest.TextTestRunner(verbosity=2).run(suite)
    
  • Admin
    Admin over 14 years
    Hi mhawke, The main got messed up in the copy and paste process. For the myClass(), the original class inherited so I forgot to delete the () when I made the small testcase. The test is supposed to fail. I'm trying to understand how the tool behaves when an assert fails. For my original post, I misunderstood that the tool is supposed to throw an exception if an assert fails. I've updated the code to catch the exception and do nothing with it. Even though the assert fails, the report says it passed. Brian
  • Admin
    Admin over 14 years
    Thank you for your help!!! I didn't catch my misunderstanding of unittest till after I posted. With the code above if I run it I get the message <testgetNameEmpty (main.test_myClass) ... ok>. I'd expect it to fail but understand why it gives the message. When I added self.fail in the except block it throws another exception. Any suggestions to allow all tests to run and report the errors at the end? I found <TestResult.addFailure> but I'm not sure how (or if I can) get to the results.
  • Alex Martelli
    Alex Martelli over 14 years
    all the testmethods DO run and report errors (E) and failures (F) [which are diagnosed by special exceptions, actually] with the normal unit-test runner (unittest.main) -- I'm surprised to hear that TextTestRunner behaves differently for you, as this is exactly what unittest.main uses (via the unittest.TestProgram class), check the sources... you DO get the expected results w/unittest.main, right?
  • Admin
    Admin over 14 years
    If I take out the try/except block and uncomment <self.assertRaises(myExcTwo,self.myClass.getName)> then it is my debugger that is stopping the execution. I ran the program from a command line it ran all the tests and gave the results I expect. This is what I was looking for. But if I ran the code as it is listed above it tells me it passes even though it should fail. I'm not worried about this since running the unit test from the command line gives me what I want. Thank you again for your help.
  • Alex Martelli
    Alex Martelli over 14 years
    Your debugger is apparently being over-eager in prematurely catching exceptions that are going to be handled just fine "upstream". Check how to configure your debugger to NOT do that!-) And no, the way you code it above it should NOT fail in any way, shape or form, as I explained: you're DELIBERATELY forcing it to succeed by catching and ignoring the exception.