Как я могу проверить, вызывается ли метод импортированного модуля во время создания объекта

Я работаю над ботом Reddit, чтобы изучить TDD в python.

У меня есть модуль с классом, который выглядит примерно так:

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 и утверждать, что методы вызываются с ожидаемыми аргументами позже. Как я могу это сделать?


person Stephen__T    schedule 19.03.2015    source источник
comment
Тот факт, что вам необходимо ссылаться на внешнюю библиотеку при выполнении модульного теста, указывает на недостаток дизайна. Вы действительно должны подумать об изменении своего кода, чтобы модульные тесты действительно могли быть модульными тестами без побочных эффектов.   -  person Joel Cornett    schedule 19.03.2015
comment
Я пытаюсь издеваться над ним, чтобы он на самом деле не вызывался. Мне нужно убедиться, что он вызывается в какой-то момент, но мне не нужно, чтобы тесты вызывали фактический API Reddit.   -  person Stephen__T    schedule 19.03.2015


Ответы (2)


Вы спрашиваете две разные вещи:

  1. Почему мое утверждение не работает
  2. Как я могу издеваться над всем Reddit

Для 1 ответ @wim правильный и точно скажет вам, как это исправить: замените тестовую строку на:

self.mocked_reddit.assert_called_with('fake', 'fakePassword')

Вызов 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 для проверки как статических методов, так и методов класса. Следуйте простому примеру:

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)
person Michele d'Amico    schedule 19.03.2015

assert_called_with является утверждением. Пока это не срабатывает, то есть не вызывает исключение AssertionError, все в порядке. Вам не нужно проверять его возвращаемое значение, которое всегда будет None.

person wim    schedule 19.03.2015
comment
Как я могу исправить весь объект Reddit в моем методе установки? - person Stephen__T; 19.03.2015