Как создать модульный тест для методов без оператора возврата в Pytest? - PullRequest
0 голосов
/ 17 апреля 2020

Я пытаюсь выполнить pytest для функции без возвращаемых значений в классе:

# app.py
from utils import DBConnection


class App:
  def add_ticket_watcher(self, ticket_key, watcher_name):
    if ticket_key is None:
      raise ValueError('Ticket key is empty')

    instance = DBConnection()
    instance.save(ticket_key, watcher_name)

Исходя из кода выше, add_ticket_watcher() - это метод, который не имеет оператора возврата. Из этой статьи я узнал, что мы можем использовать mocks для имитации c ожидаемого поведения этого метода.

Для функции mocking с pytest я обнаружил, что мы можем использовать monkeypatch .

Итак, мой подход состоит в том, чтобы выполнить 2 контрольных примера для add_ticket_watcher():

  • проверить действительный ключ заявки
  • проверить недействительный ключ заявки

У меня есть что-то вроде:

# test_app.py
import pytest
from src.app import App


@pytest.fixture
def app():
    app = App()
    return app


# Positive test case: Setup parameterize test for valid ticket key
add_valid_watcher_data = (
    ('func_name', 'ticket_key', 'watcher_name', 'expected_result', 'comment'),
    [
        ('add_ticket_watcher', 'TIC-13527', 'someone', None, 'add watcher at valid ticket key'),
    ]
)


@pytest.mark.parametrize(*add_valid_watcher_data)
def test_add_ticket_watcher(monkeypatch, app, func_name, ticket_key, watcher_name, expected_result, comment):

    def mock_add_ticket_watcher(ticket_key, watcher_name):
        # Since `add_ticket_watcher()` has no return statement, I'm mocking the result as None
        return None

    monkeypatch.setattr(app, "add_ticket_watcher", mock_add_ticket_watcher)

    # Run each input parameter from add_valid_watcher_data to `add_ticket_watcher()`
    watcher = getattr(app, func_name)(ticket_key, watcher_name)

    # If watcher has None value, then `add_ticket_watcher()` execution is successful.
    assert expected_result == watcher


# Negative test case: Setup parameterize test for invalid ticket key
add_invalid_watcher_data = (
    ('func_name', 'ticket_key', 'watcher_name', 'exception_message', 'comment'),
    [
        ('add_ticket_watcher', 'TIC-xx', 'someone', 'Ticket key is empty', 'add watcher at invalid ticket key'),
        ('add_ticket_watcher', None, 'someone', 'Ticket key is empty', 'ticket key has None value'),
    ]
)


@pytest.mark.parametrize(*add_invalid_watcher_data)
def test_exception_add_ticket_watcher(app, func_name, ticket_key, watcher_name, exception_message, comment):
    with pytest.raises(ValueError, match=exception_message):
      # Run each input parameter from add_invalid_watcher_data to `add_ticket_watcher()`
      getattr(app, func_name)(ticket_key, watcher_name)

В test_add_ticket_watcher() я не уверен, что утверждать. Однако, поскольку App.add_ticket_watcher(ticket_key, watcher_name) не имеет оператора возврата. Я создаю фиктивную функцию для возврата None.

Есть ли лучший способ достичь той же цели?

Как создать модульный тест для методов без оператора возврата в Pytest?

Ответы [ 2 ]

0 голосов
/ 18 апреля 2020

Я не знаком с pytest, но unittest. Однако я бы написал эти 3 теста для вашей функции (с небольшим изменением существующей функции:))

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

app.py

from utils import DBConnection
import cx_Oracle

class App:

  def add_ticket_watcher(self, ticket_key, watcher_name):
    if ticket_key is None:
      raise ValueError('Ticket key is empty')

    instance = DBConnection()

    try:
        instance.save(ticket_key, watcher_name)
    except Exception as e:
        raise cx_Oracle.DatabaseError('Database save failed')

test_app.py

import unittest
import app
import cx_Oracle
from mock import patch

class TestApp(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.app = app.App()

    def test_if_exception_raised_when_ticket_value_is_none(self):
        with self.assertRaises(ValueError):
            self.app.add_ticket_watcher(None, 'dummyname')

    def test_if_dbconnection_save_is_called(self):
        with patch('app.DBConnection.save') as mock_dbconn_save:
            self.app.add_ticket_watcher(123, 'dummyname')
            self.assertTrue(mock_dbconn_save.called)

    def test_if_dbconnection_save_raises_error(self):
        with patch('app.DBConnection.save', side_effect = Exception) as mock_dbconn_save_exc:
            with self.assertRaises(cx_Oracle.DatabaseError):
                self.app.add_ticket_watcher(123, 'dummyname')

if __name__ == '__main__':
    unittest.main(verbosity=2)
0 голосов
/ 17 апреля 2020

В дополнение к тому, что упомянул Чепнер. Вы можете проверить, вызывается ли logger.info с помощью mock. А чтобы протестировать негативный сценарий, вы можете заставить его вызвать исключение с помощью mock side_effects, а затем проверить, вызывается ли logger.exception.

...