python-3.x - Python 测试对实例化为另一个类中的变量的类方法的调用

标签 python-3.x unit-testing pytest python-unittest

我有一个类 A,它在其构造函数中接收另一个类 B 的配置。 A 然后通过使用此配置和创建类 B 的实例从 B 创建一个变量。 稍后在代码中将此变量称为 self.b.do_somthing()

我怎样才能确保这个方法调用实际上是用 mock/patch 调用的?下面我发布了一个 super 简单的虚拟代码来做类似的事情。我想测试 perform() 实际上调用了 sing_a_song()

请注意代码中的注释

my_app.py

class Sing:
    def __init__(self, **config):
        self.songs = {
            "almost home": """
                Almost home
                Brother, it won't be long
                Soon all your burdens will be gone
                With all your strength""",
            "smooth criminal": """
                Annie, are you okay?
                So, Annie, are you okay? Are you okay, Annie?
            """
        }
        self.config = config

    def sing_a_song(self, song_name):
        return self.songs.get(song_name.lower())


class Singer:
    def __init__(self, song_to_sing):
        self.song_to_sing = song_to_sing
        self.sing = Sing(**some_config_that_doesnt_matter)  # <-- this part is the culprit

    def perform(self):
        if self.song_to_sing.lower() == "smooth criminal":
            self.sing.sing_a_song(self.song_to_sing)   # <-- and it's called here
        else:
            return "I don't know this song, not gonna perform."

和测试(不起作用)

测试.py

import unittest
from unittest.mock import patch
from my_app import Singer


class TestStringMethods(unittest.TestCase):
    @patch("my_app.Sing", autospec=True)
    def test_download(self, sing_mock):
        melissa = Singer("smooth criminal")
        melissa.perform()

        sing_mock.assert_called()  # <-- works  (because of the call inside the constructor or bc of the call inside perform() ???
        sing_mock.sing_a_song.assert_called()   # <-- returns AssertionError: Expected 'sing_a_song' to have been called. 
        sing_mock.assert_called_with("smooth criminal")   # <-- returns AssertionError: Expected call: Sing('smooth criminal') ... Actual call: Sing()


        # If I do the following, the test passes, BUT if i after that REMOVE the actual
        # call to self.sing.sing_a_song(self.song_to_sing), it still passes ??!?!!?
        sing_mock.sing_a_song("smooth criminal")
 

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

另外..我意识到调用实际上是对类而不是对象的方法进行的,就好像该方法是静态的一样,但它不是这样定义的,因此我需要以某种方式解决这个问题。

所以..我做错了什么,请帮忙。我对 mocking 和 patching 还是个新手,无论我读了多少文章,我都很困惑。

最佳答案

如果你正在模拟一个类,并且想检查对该类的方法调用,你必须在该类的实例上检查它们。您可以在 mock 上使用 return_value 获取 mock 的类实例(这通常会为您提供调用运算符的结果,在类的情况下是类实例化)。请注意,实例化特定类模拟将始终为您提供相同的“实例”(例如另一个模拟)。

因此,在您的情况下,以下将起作用:

class TestStringMethods(unittest.TestCase):
    @patch("my_app.Sing", autospec=True)
    def test_download(self, sing_mock):
        melissa = Singer("smooth criminal")
        melissa.perform()

        sing_mock.assert_called()  # this asserts that the class has been instantiated
        sing_mock_instance = sing_mock.return_value  # split off for readability
        sing_mock_instance.sing_a_song.assert_called()
        sing_mock_instance.sing_a_song.assert_called_with("smooth criminal")   

关于python-3.x - Python 测试对实例化为另一个类中的变量的类方法的调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71369147/

相关文章:

ios - 单元测试按钮单击不起作用

java - 一种主要方法的单元测试

c# - 如何使用 ActionResult<T> 进行单元测试?

python - pytest 是否具有 assertItemsEqual/assertCountEqual 等价物

pytest doctest 与 conftest.py 中的固定装置

python-3.x - Python3 deepcopy 的备忘录和 nil 参数的文档不完整和/或循环?

python - 类型错误 : '<=' not supported between instances of 'str' and 'int'

python-3.x - Pytest Cov 报告缺少模拟异常返回

python - 根据列中的重复值有条件地复制数据

python-3.x - unittest - 检查之前测试的结果