Как проверить, была ли функция поражена с помощью pytest

#python #testing #pytest #monkeypatching

#python #тестирование #pytest #monkeypatching

Вопрос:

Я пробовал это:

 def test_send_confirm_hit(monkeypatch):
    hit = False
    def called():
        global hit
        hit = True

    monkeypatch.setattr("web.email.send_confirm", called)

    # ... some event that will cause web.email.send_confirm to be hit

    assert hit  # verify send_confirm was hit
  

Похоже, это работает, хотя я бы предпочел не использовать глобальную переменную. Каков наилучший способ сделать это?

Комментарии:

1. Используйте a unittest.mock.Mock и проверьте .assert_called() метод.

2. Я бы хотел избежать использования unittest

3. Вам не нужно писать тесты unittest, вы можете использовать его с pytest.

4. Вы могли бы установить pypi.org/project/mock и используйте from mock import MagicMock , но это всего лишь бэкпорт для более старых версий python, а в более новых версиях он просто псевдонимы в unittest любом случае… так что не делайте этого

5. @L3viathan Это так полезно и открывает глаза! Я думал, что не могу смешивать pytest тесты с чем-либо из встроенного unittest мира.

Ответ №1:

Если вы используете «правильный» макет, он поставляется с .assert_called() методом:

 import unittest.mock


def test_send_confirm_hit(monkeypatch):
    mock_send_confirm = unittest.mock.Mock()

    monkeypatch.setattr("web.email.send_confirm", mock_send_confirm)

    # ... some event that will cause web.email.send_confirm to be hit

    mock_send_confirm.assert_called()
  

Комментарии:

1. Я понятия не имею, почему, но это не работает. Я добавил точку останова при запуске тестов в режиме отладки, и я знаю, что она вызывает этот метод….

2. Как вы импортируете send_confirm функцию, которую хотите протестировать? Если вы это сделаете from web.email import send_confirm , monkeypatching не будет работать, потому что функция уже имеет ссылку на real send_confirm . Что-то вроде from web import email; email.send_confirm() will .

3. это так странно, я не знал, что это такое! Спасибо за это!

Ответ №2:

Вы могли бы использовать макет вместо функции:

 from unittest.mock import MagicMock


def test_send_confirm_hit(monkeypatch):
    called = MagicMock()

    monkeypatch.setattr("web.email.send_confirm", called)

    # ... some event that will cause web.email.send_confirm to be hit

    assert called.call_count == 1  # verify send_confirm was hit
  

Если вам действительно нужно выполнить какую-то пользовательскую логику, как вы бы сделали с функцией, вы можете добавить a side_effect в макет:

 def test_send_confirm_hit(monkeypatch):
    def side_effect():
        return 42

    called = MagicMock(side_effect=side_effect)