To put concisely, I have a class A whose constructor catches any exceptions that might occur. The only logic that can throw an exception though, is the construction of another class B (from an external library, in case that matters). I want to test that when this inner constructor (B) throws an exception, the outer constructor (A) catches this exception.
# module_a.py
from external_module_b import B
class A:
def __init__(self) -> None:
try:
self.b = B(
b_param_1="...",
b_param_2="..."
)
self.ok = True
# ...
except Exception as e:
self.ok = False
print_report_request(repr(e))
First, I tried using @patch() with side_effect like this:
# test_module_a.py
from unittest import mock, TestCase
from module_a import A
class MyTestCase(TestCase):
@mock.patch("external_module_b.B")
def test_constructor_throws(self, mock_b: mock.Mock):
mock_b.side_effect = Exception("test")
a = A()
self.assertFalse(a.ok)
This didn't seem to work—a.ok was True. I tried another suggestion to define side_effect in @patch() itself:
@mock.patch("external_module_b.B", side_effect=Exception("Test"))
def test_constructor_throws(self, mock_b: mock.Mock):
a = A()
self.assertFalse(a.ok)
a.ok was still True.
I wondered if something's wrong with the string I'm giving to @patch(). But typing "external_module_b" in code, PyCharm's autocomplete did suggest "external_module_b.B". (I'm not sure whether that's a valid proof or not.) I tried yet another suggestion that uses raiseError. I also tried making side_effect a function (lambda). But I think it's more likely that I'm misunderstanding something fundamental. Possibly to do with mocking constructors / classes.
Ah. Figured it out. It had nothing to do with constructors or the side_effect syntax or autospec'ing or any of the things I was trying. Reading Where to patch was the key. I'll explain in a way that made sense to me (as a beginner, for beginners):
I thought in @patch() I was supposed to refer to the "full path" of the class I wanted to affect— ie where it came from originally , eg "external_module_b.B".
But this only does its thing when the code you want to test also constructs the class from its original source , eg constructs via external_module_b.B().
But in my case, I had imported B into module_a , and constructed that via B(). So it's actually module_a.B that I needed to patch, not external_module_b.B.
So this worked for me:
# test_module_a.py
from unittest import mock, TestCase
from module_a import A
class MyTestCase(TestCase):
@mock.patch("module_a.B") # <-- This was the only change I needed.
def test_constructor_throws(self, mock_b: mock.Mock):
mock_b.side_effect = Exception("test")
a = A()
self.assertFalse(a.ok)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.