Python 正在测试对 class 方法的调用,该方法实例化为另一个 class 中的变量
Python testing a call to a class's method instantiated as a varable in another class
我有一个 class A
,它在其构造函数中接收另一个 class B 的配置。 A
然后使用此配置从 B
中创建一个变量并创建 class B
的实例。
稍后在代码中它将此变量称为 self.b.do_somthing()
.
如何确保此方法调用实际上是用 mock/patch 调用的?下面我 post 一个超级简单的虚拟代码做类似的事情。我想测试 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."
和测试(不起作用)
test.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()
另外..我意识到调用实际上是对 class 而不是对象的方法进行的,就好像该方法是静态的一样,但它不是这样定义的,因此我需要以某种方式解决这个问题。
所以..我做错了什么,请帮忙。我对 mocking 和 patching 还是个新手,不管读了多少文章我都很困惑。
如果您正在模拟一个 class,并且想要检查对该 class 的方法调用,您必须在 class 的实例上检查它们。您在模拟上使用 return_value
获得模拟的 class 实例(通常会为您提供调用运算符的结果,在 class 的情况下是 class实例化)。请注意,实例化一个特定的 class 模拟将始终为您提供相同的“实例”(例如另一个模拟)。
因此,对于您的情况,以下方法将起作用:
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")
另一种方法适用于模拟整个 class Sing
。但是,根据您想要准确测试的内容,您可能只想模拟特定方法:
class TestStringMethods(unittest.TestCase):
@patch('my_app.Sing.sing_a_song')
def test_download(self, sing_a_song_mock):
melissa = Singer('smooth criminal')
melissa.perform()
sing_a_song_mock.assert_called() # this works
sing_a_song_mock.assert_called_with('smooth criminal') # works, too
例如,这在某些情况下可能很有用,因为您已经知道在其他测试中测试了 class 的实例化,而您现在只想测试 [=16] 的确切调用=] 当前测试中的方法。
我有一个 class A
,它在其构造函数中接收另一个 class B 的配置。 A
然后使用此配置从 B
中创建一个变量并创建 class B
的实例。
稍后在代码中它将此变量称为 self.b.do_somthing()
.
如何确保此方法调用实际上是用 mock/patch 调用的?下面我 post 一个超级简单的虚拟代码做类似的事情。我想测试 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."
和测试(不起作用)
test.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()
另外..我意识到调用实际上是对 class 而不是对象的方法进行的,就好像该方法是静态的一样,但它不是这样定义的,因此我需要以某种方式解决这个问题。
所以..我做错了什么,请帮忙。我对 mocking 和 patching 还是个新手,不管读了多少文章我都很困惑。
如果您正在模拟一个 class,并且想要检查对该 class 的方法调用,您必须在 class 的实例上检查它们。您在模拟上使用 return_value
获得模拟的 class 实例(通常会为您提供调用运算符的结果,在 class 的情况下是 class实例化)。请注意,实例化一个特定的 class 模拟将始终为您提供相同的“实例”(例如另一个模拟)。
因此,对于您的情况,以下方法将起作用:
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")
另一种方法适用于模拟整个 class Sing
。但是,根据您想要准确测试的内容,您可能只想模拟特定方法:
class TestStringMethods(unittest.TestCase):
@patch('my_app.Sing.sing_a_song')
def test_download(self, sing_a_song_mock):
melissa = Singer('smooth criminal')
melissa.perform()
sing_a_song_mock.assert_called() # this works
sing_a_song_mock.assert_called_with('smooth criminal') # works, too
例如,这在某些情况下可能很有用,因为您已经知道在其他测试中测试了 class 的实例化,而您现在只想测试 [=16] 的确切调用=] 当前测试中的方法。