简体   繁体   English

Python:模拟 class 构造函数在另一个类的构造函数中抛出异常

[英]Python: Mock class constructor to throw an Exception, inside another class's constructor

Problem问题

To put concisely, I have a class A whose constructor catches any exceptions that might occur.简而言之,我有一个 class A,其构造函数捕获可能发生的任何异常。 The only logic that can throw an exception though, is the construction of another class B (from an external library, in case that matters).但是,唯一可以抛出异常的逻辑是构造另一个 class B(来自外部库,以防万一)。 I want to test that when this inner constructor (B) throws an exception, the outer constructor (A) catches this exception.我想测试当这个内部构造函数(B)抛出异常时,外部构造函数(A)捕获这个异常。

# 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))

Attempts尝试

First, I tried using @patch() with side_effect like this:首先,我尝试将 @patch() 与 side_effect 一起使用,如下所示:

# 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.这似乎不起作用——a.ok 是正确的。 I tried another suggestion to define side_effect in @patch() itself:我尝试了另一个在 @patch() 本身中定义 side_effect 的建议:

    @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. a.ok 仍然是 True。

I wondered if something's wrong with the string I'm giving to @patch().我想知道我提供给@patch() 的字符串是否有问题。 But typing "external_module_b" in code, PyCharm's autocomplete did suggest "external_module_b.B".但是在代码中输入“external_module_b”,PyCharm 的自动完成确实建议“external_module_b.B”。 (I'm not sure whether that's a valid proof or not.) I tried yet another suggestion that uses raiseError. (我不确定这是否是有效证据。)我尝试了另一个使用 raiseError 的建议。 I also tried making side_effect a function (lambda).我还尝试将 side_effect 设为 function (lambda)。 But I think it's more likely that I'm misunderstanding something fundamental.但我认为我更有可能误解了一些基本的东西。 Possibly to do with mocking constructors / classes.可能与 mocking 构造函数/类有关。

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.它与构造函数或 side_effect 语法或自动指定或我正在尝试的任何事情无关。 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".我想在@patch() 中我应该引用我想要影响的 class 的“完整路径”——最初来自哪里,例如“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().但这仅在您要测试的代码从其原始来源构造 class 时才会发挥作用,例如通过 external_module_b.B() 构造。

But in my case, I had imported B into module_a , and constructed that via B().但就我而言,我已将B 导入到 module_a中,并通过 B() 构建 So it's actually module_a.B that I needed to patch, not external_module_b.B.所以我需要修补的实际上是 module_a.B,而不是 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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM