如何测试在对象构造期间调用导入模块上的方法
How can I test that a method on an imported module is called during object construction
我正在开发一个 Reddit 机器人来学习 python 中的 TDD。
我有一个带有 class 的模块,它是这样的:
from praw import Reddit
class Bot():
def __init__(self):
self.reddit = Reddit(user_agent='myBot')
self.reddit.login('fake', 'fakePassword')
在我的测试套件中,我有一个这样的设置方法:
@patch('bot.bot.Reddit.login')
def setUp(self, mocked_reddit):
self.mocked_reddit = mocked_reddit
self.subject = Bot()
..和这样的测试:
def should_call_reddit_login_when_initialized_test(self):
self.assertTrue(self.mocked_reddit.assert_called_with('fake', 'fakePassword'))
我的补丁似乎只部分起作用。它实际上并没有调用 Reddit API 这很好。然而,断言总是错误的。
line 22 in should_call_reddit_login_when_initialized_test
self.assertTrue(self.mocked_reddit.assert_called_with('fake', 'fakePassword'))
AssertionError: None is not true
理想情况下,我可以模拟整个 Reddit class 并断言稍后使用预期参数调用这些方法。我怎样才能做到这一点?
assert_called_with
是断言。只要那没有触发,即引发 AssertionError
异常,那么你就可以了。你不需要检查它的 return 值,它总是 None
.
你问的是两件不同的事情:
- 为什么我的断言不起作用
- 如何模拟整个
Reddit
对于 1 @wim 答案是正确的并告诉你如何修复它:将你的测试线替换为:
self.mocked_reddit.assert_called_with('fake', 'fakePassword')
assert_*
mock 的调用在 assert 失败时已经引发异常。
要处理 2,您应该改为修补 'bot.bot.Reddit'
,并考虑到您的对象将是模拟的 return_value
。在这种情况下,我强烈建议使用 autospec=True
来保留 Reddit
完整的签名,请查看 Autospeccing 以了解更多详细信息。
在这种情况下,您的测试变为:
@patch('bot.bot.Reddit', autospec=True)
def setUp(self, mocked_reddit_class):
self.mocked_reddit_class = mocked_reddit_class
self.mocked_reddit = mocked_reddit_class.return_value
self.subject = Bot()
def should_call_reddit_login_when_initialized_test(self):
self.mocked_reddit.login.assert_called_with('fake', 'fakePassword')
在这种情况下,您应该注意使用 self.mocked_reddit
检查对象方法,并使用 self.mocked_reddit_class
检查静态和 class 方法。举个简单的例子:
import unittest
from unittest.mock import patch
class A():
def b(self, a, b):
pass
@classmethod
def c(cls,a,b):
pass
@staticmethod
def d(a,b):
pass
def ab(a,b):
return A().b(a,b)
def ac(a,b):
return A.c(a,b)
def ad(a,b):
return A.d(a,b)
class MyTestCase(unittest.TestCase):
@patch(__name__+".A", autospec=True)
def test_something(self,mock_a):
ab(1,2)
mock_a.return_value.b.assert_called_with(1,2)
ac(1,2)
self.assertFalse(mock_a.return_value.c.called)
mock_a.c.assert_called_with(1,2)
ad(1,2)
self.assertFalse(mock_a.return_value.d.called)
mock_a.d.assert_called_with(1,2)
我正在开发一个 Reddit 机器人来学习 python 中的 TDD。
我有一个带有 class 的模块,它是这样的:
from praw import Reddit
class Bot():
def __init__(self):
self.reddit = Reddit(user_agent='myBot')
self.reddit.login('fake', 'fakePassword')
在我的测试套件中,我有一个这样的设置方法:
@patch('bot.bot.Reddit.login')
def setUp(self, mocked_reddit):
self.mocked_reddit = mocked_reddit
self.subject = Bot()
..和这样的测试:
def should_call_reddit_login_when_initialized_test(self):
self.assertTrue(self.mocked_reddit.assert_called_with('fake', 'fakePassword'))
我的补丁似乎只部分起作用。它实际上并没有调用 Reddit API 这很好。然而,断言总是错误的。
line 22 in should_call_reddit_login_when_initialized_test
self.assertTrue(self.mocked_reddit.assert_called_with('fake', 'fakePassword'))
AssertionError: None is not true
理想情况下,我可以模拟整个 Reddit class 并断言稍后使用预期参数调用这些方法。我怎样才能做到这一点?
assert_called_with
是断言。只要那没有触发,即引发 AssertionError
异常,那么你就可以了。你不需要检查它的 return 值,它总是 None
.
你问的是两件不同的事情:
- 为什么我的断言不起作用
- 如何模拟整个
Reddit
对于 1 @wim 答案是正确的并告诉你如何修复它:将你的测试线替换为:
self.mocked_reddit.assert_called_with('fake', 'fakePassword')
assert_*
mock 的调用在 assert 失败时已经引发异常。
要处理 2,您应该改为修补 'bot.bot.Reddit'
,并考虑到您的对象将是模拟的 return_value
。在这种情况下,我强烈建议使用 autospec=True
来保留 Reddit
完整的签名,请查看 Autospeccing 以了解更多详细信息。
在这种情况下,您的测试变为:
@patch('bot.bot.Reddit', autospec=True)
def setUp(self, mocked_reddit_class):
self.mocked_reddit_class = mocked_reddit_class
self.mocked_reddit = mocked_reddit_class.return_value
self.subject = Bot()
def should_call_reddit_login_when_initialized_test(self):
self.mocked_reddit.login.assert_called_with('fake', 'fakePassword')
在这种情况下,您应该注意使用 self.mocked_reddit
检查对象方法,并使用 self.mocked_reddit_class
检查静态和 class 方法。举个简单的例子:
import unittest
from unittest.mock import patch
class A():
def b(self, a, b):
pass
@classmethod
def c(cls,a,b):
pass
@staticmethod
def d(a,b):
pass
def ab(a,b):
return A().b(a,b)
def ac(a,b):
return A.c(a,b)
def ad(a,b):
return A.d(a,b)
class MyTestCase(unittest.TestCase):
@patch(__name__+".A", autospec=True)
def test_something(self,mock_a):
ab(1,2)
mock_a.return_value.b.assert_called_with(1,2)
ac(1,2)
self.assertFalse(mock_a.return_value.c.called)
mock_a.c.assert_called_with(1,2)
ad(1,2)
self.assertFalse(mock_a.return_value.d.called)
mock_a.d.assert_called_with(1,2)