如何测试在对象构造期间调用导入模块上的方法

时间:2015-03-19 03:54:11

标签: python unit-testing python-3.x mocking python-unittest

我正在使用Reddit机器人来学习python中的TDD。

我有一个类似于以下类的模块:

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类,并断言以后使用预期参数调用方法。我怎么能做到这一点?

2 个答案:

答案 0 :(得分:4)

assert_called_with 断言。只要没有触发,即提出AssertionError例外,那么你就行了。您无需检查其返回值,该值始终为None

答案 1 :(得分:2)

你问两件事:

  1. 为什么我的断言不起作用
  2. 如何模拟整个Reddit
  3. 对于 1 @wim的回答是正确的,并告诉您具体如何修复它:用以下代码替换您的测试行:

    self.mocked_reddit.assert_called_with('fake', 'fakePassword')
    
    断言失败时,

    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来检查静态和类方法。举个简单的例子:

    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)