Can't catch mocked exception because it doesn't inherit BaseException Can't catch mocked exception because it doesn't inherit BaseException python python

Can't catch mocked exception because it doesn't inherit BaseException


I could reproduce the error with a minimal example:

foo.py:

class MyError(Exception):    passclass A:    def inner(self):        err = MyError("FOO")        print(type(err))        raise err    def outer(self):        try:            self.inner()        except MyError as err:            print ("catched ", err)        return "OK"

Test without mocking :

class FooTest(unittest.TestCase):    def test_inner(self):        a = foo.A()        self.assertRaises(foo.MyError, a.inner)    def test_outer(self):        a = foo.A()        self.assertEquals("OK", a.outer())

Ok, all is fine, both test pass

The problem comes with the mocks. As soon as the class MyError is mocked, the expect clause cannot catch anything and I get same error as the example from the question :

class FooTest(unittest.TestCase):    def test_inner(self):        a = foo.A()        self.assertRaises(foo.MyError, a.inner)    def test_outer(self):        with unittest.mock.patch('foo.MyError'):            a = exc2.A()            self.assertEquals("OK", a.outer())

Immediately gives :

ERROR: test_outer (__main__.FooTest)----------------------------------------------------------------------Traceback (most recent call last):  File "...\foo.py", line 11, in outer    self.inner()  File "...\foo.py", line 8, in inner    raise errTypeError: exceptions must derive from BaseExceptionDuring handling of the above exception, another exception occurred:Traceback (most recent call last):  File "<pyshell#78>", line 8, in test_outer  File "...\foo.py", line 12, in outer    except MyError as err:TypeError: catching classes that do not inherit from BaseException is not allowed

Here I get a first TypeErrorthat you did not have, because I am raising a mock while you forced a true exception with 'requests.exceptions.ConnectionError': requests.exceptions.ConnectionError in config. But the problem remains that the except clause tries to catch a mock.

TL/DR: as you mock the full requests package, the except requests.exceptions.ConnectionError clause tries to catch a mock. As the mock is not really a BaseException, it causes the error.

The only solution I can imagine is not to mock the full requests but only the parts that are not exceptions. I must admit I could not find how to say to mock mock everything except this but in your example, you only need to patch requests.head. So I think that this should work :

def test_bad_connection(self):    with mock.patch('path.to.my.package.requests.head',                    side_effect=requests.exceptions.ConnectionError):        self.assertEqual(            mypackage.myMethod('some_address',            mypackage.successfulConnection.FAILURE        )

That is : only patch the head method with the exception as side effect.


I just ran into the same issue while trying to mock sqlite3 (and found this post while looking for solutions).

What Serge said is correct:

TL/DR: as you mock the full requests package, the except requests.exceptions.ConnectionError clause tries to catch a mock. As the mock is not really a BaseException, it causes the error.

The only solution I can imagine is not to mock the full requests but only the parts that are not exceptions. I must admit I could not find how to say to mock mock everything except this

My solution was to mock the entire module, then set the mock attribute for the exception to be equal to the exception in the real class, effectively "un-mocking" the exception. For example, in my case:

@mock.patch(MyClass.sqlite3)def test_connect_fail(self, mock_sqlite3):    mock_sqlite3.connect.side_effect = sqlite3.OperationalError()    mock_sqlite3.OperationalError = sqlite3.OperationalError    self.assertRaises(sqlite3.OperationalError, MyClass, self.db_filename)

For requests, you could assign exceptions individually like this:

    mock_requests.exceptions.ConnectionError = requests.exceptions.ConnectionError

or do it for all of the requests exceptions like this:

    mock_requests.exceptions = requests.exceptions

I don't know if this is the "right" way to do it, but so far it seems to work for me without any issue.


For those of us who need to mock an exception and can't do that by simply patching head, here is an easy solution that replaces the target exception with an empty one:

Say we have a generic unit to test with an exception we have to have mocked:

# app/foo_file.pydef test_me():    try:       foo()       return "No foo error happened"    except CustomError:  # <-- Mock me!        return "The foo error was caught"

We want to mock CustomError but because it is an exception we run into trouble if we try to patch it like everything else. Normally, a call to patch replaces the target with a MagicMock but that won't work here. Mocks are nifty, but they do not behave like exceptions do. Rather than patching with a mock, let's give it a stub exception instead. We'll do that in our test file.

# app/test_foo_file.pyfrom mock import patch# A do-nothing exception we are going to replace CustomError withclass StubException(Exception):    pass# Now apply it to our test@patch('app.foo_file.foo')@patch('app.foo_file.CustomError', new_callable=lambda: StubException)def test_foo(stub_exception, mock_foo):    mock_foo.side_effect = stub_exception("Stub")  # Raise our stub to be caught by CustomError    assert test_me() == "The error was caught"# Success!

So what's with the lambda? The new_callable param calls whatever we give it and replaces the target with the return of that call. If we pass our StubException class straight, it will call the class's constructor and patch our target object with an exception instance rather than a class which isn't what we want. By wrapping it with lambda, it returns our class as we intend.

Once our patching is done, the stub_exception object (which is literally our StubException class) can be raised and caught as if it were the CustomError. Neat!