Рубрики
Без рубрики

Особенности Ptyest, которые вам нужны в вашем (тестировании) жизни

Примечание. Первоначально это было опубликовано на Martinheinz.dev Testing Ваш код является неотъемлемой частью разработки … Теги с Python, тестированием, учебником, обучением.

Примечание: это было первоначально опубликовано в martinheinz.dev

Тестирование вашего кода является неотъемлемой частью разработки и тесты качества могут сэкономить вам большую головную боль в по дороге. Есть много гидов на тестировании в Python и конкретно тестирование с Питиш , Но есть довольно много особенностей, которые я редко вижу упомянул где угодно, но мне часто нужен. Итак, здесь идет список трюков и особенности Pteest , что я не могу жить без (и ты тоже не сможешь – Скоро ).

Тестирование для исключения

Давайте начнем просто. У вас есть функция, которая бросает исключение, и вы хотите убедиться, что это происходит в правильном условиях и включает в себя правильное сообщение:

import pytest

def test_my_function():
    with pytest.raises(Exception, match='My Message') as e:
        my_function()
        assert e.type is ValueError

Здесь мы можем увидеть простой контекстный менеджер что Pteest обеспечивает нам. Это позволяет нам указывать тип исключения, который должен быть поднят, а также сообщение указанного исключения. Если исключение не поднимается в блоке, то тест не удается. Вы также можете проверить больше атрибутов исключения, поскольку контекстный менеджер возвращает ExceptionInfo класс, который имеет такие атрибуты, как Тип , ценить или Traceback Отказ

Фильтрация предупреждений

За исключением, давайте посмотрим на предупреждения. Иногда вы получаете кучу предупреждающих сообщений в ваших журналах изнутри библиотек, которые вы используете. Вы не можете исправить их, и они действительно просто создают ненужный шум, поэтому давайте избавимся от них:

import warnings
from sqlalchemy.exc import SADeprecationWarning

def test_my_function():
    # SADeprecationWarning is issued by SQLAchemy when deprecated API is used
    warnings.filterwarnings("ignore", category=SADeprecationWarning)
    ...
    assert ...

def test_my_function():
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter("ignore")  # ignore annoying warnings
        ...
        assert ...
    # warnings restored

Здесь мы показываем 2 подхода – в первую очередь, мы прямо игнорируем все предупреждения о указанной категории, вставляя фильтр в передней части списка фильтров. Это приведет к тому, что ваша программа игнорирует все предупреждения этой категории, пока она не заканчивается, что может не всегда желательно. Со вторым подходом мы используем контекстный менеджер, который восстанавливает все предупреждения после выхода его объема. Мы также указываем запись = Правда Так что мы можем проверить список выпущенных (игнорируемых) предупреждений, если это будет.

Тестирование стандартных выходных и стандартных сообщений об ошибках

Далее давайте посмотрим на следующий сценарий: у вас есть инструмент командной строки, который имеет кучу функций, которые печатают сообщения на стандартный выход, но не возвращайте ничего. Итак, как мы это проверим?

def test_my_function(capsys):
    my_function()  # function that prints stuff
    captured = capsys.readouterr()  # Capture output
    assert f"Received invalid message ..." in captured.out  # Test stdout
    assert f"Fatal error ..." in captured.err  # Test stderr

Решить это, Pteest Предоставляет крепеж под названием Капсы , который – хорошо – захватывает вывод системы. Все, что вам нужно использовать, – это добавить его в качестве параметра в свою тестовую функцию. Далее, после вызова функции, которая проверяется, вы захватите выходы в форме кортежа – (OUT, ERR) , который вы можете использовать в утверждении Assert.

Исправления предметов

Иногда при тестировании вам могут потребоваться заменить объекты, используемые в функциях, в соответствии с тестом, чтобы обеспечить более предсказуемый набор данных или избежать упомянутых функций доступа к ресурсам, которые могут быть недоступны. mock.patch может помочь с этим:

from unittest import mock

def test_my_function():
    with mock.patch('module.some_function') as some_function:  # Used as context manager
        my_function()  # function that calls `some_function`

        some_function.assert_called_once()
        some_function.assert_called_with(2, 'x')

@mock.patch('module.func')  # Used as decorator
def test_my_function(some_function):
    module.func(10)  # Calls patched function
    some_function.assert_called_with(10)  # True

В этом первом примере мы можем видеть, что можно исправлять функции, а затем проверять, сколько раз и с какими аргументами их называли. Эти патчи также могут быть сложены как в виде декоратора, так и контекстно-менеджера. Теперь для некоторых более мощных применений:

from unittest import mock

def test_my_function():
    with mock.patch.object(SomeClass, 'method_of_class', return_value=None) as mock_method:
        instance = SomeClass()
        instance.method_of_class('arg')

        mock_method.assert_called_with('arg')  # True

def test_my_function():
    r = Mock()
    r.content = b'{"success": true}'
    with mock.patch('requests.get', return_value=r) as get:  # Avoid doing actual GET request
        some_function()  # Function that calls requests.get
        get.assert_called_once()

Первый пример в приведенном выше фрагменте довольно просты – мы заменяем метод SomeClass и сделать его возвращением Нет Отказ Во втором, более практическом примере мы избегаем зависящего от удаленного API/ресурса, заменяя requests.get издевательства и сделав его возврат объекта, который мы поставляем с подходящими данными.

Есть еще много вещей, которые Макет Модуль может сделать для вас, а некоторые из них довольно дикие, включая побочные эффекты, издевательства на свойства, издевательства несуществующих атрибутов и многое другое. Если вы столкнулись с проблемой при написании тестов, то вы должны определенно оформить заказ Документы Для этого модуля, потому что вы можете очень хорошо найти решение там.

Обмен приспособлениями с Conftest.py

Если вы пишете много тестов, то в какой-то момент вы поймете, что было бы неплохо иметь все Pteest Светильники в одном месте, из которого вы сможете импортировать их и, следовательно, делиться через тестовые файлы. Это можно решить с Conftest.py Отказ

Conftest.py это файл, который находится в базе дерева вашего тестового каталога. В этом файле вы можете хранить все тестовые приборы, и они затем автоматически обнаружены Питиш Так что вам даже не нужно их импортировать.

Это также полезно, если вам нужно делиться данными между несколькими тестами – просто создайте прибор, который возвращает тестовые данные.

Другими полезными функциями являются способность указывать область приспособления – это важно, когда у вас есть приспособления, которые очень дорого создают, например, соединения с базой данных ( сессия область действия) и на другом конце спектра есть тот, который нужно Чтобы сбросить после каждого тестового корпуса ( Функция Область применения). Возможные значения для крепления Область являются: Функция , класс , модуль , Пакет и сессия .

Параметрирующие приспособления

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

import pytest, os

@pytest.fixture(scope='function')
def reset_sqlite_db(request):
    path = request.param  # Path to database file
    with open(path, 'w'): pass
    yield None
    os.remove(path)

@pytest.mark.parametrize('reset_sqlite_db', ['/tmp/test_db.sql'], indirect=True)
def test_send_message(reset_sqlite_db):
    ...  # Perform tests that access prepared SQLite database

Выше приведен пример крепежа, который готовит базу данных тестирования SQLite для каждого теста. Этот прибор получает путь к базе данных в качестве параметра. Этот путь передан в прибор, используя Запрос объект, какой атрибут Param Имеется ли у всех аргументов, переданных к приспособлению, в этом случае только один – путь. Здесь этот прибор сначала создает файл базы данных (и может также заполнить его – опущено для ясности), затем дает выполнение к тесту и после завершения теста, прибор удаляет файл базы данных.

Что касается самого теста, мы используем @ pytest.mark.parametrize. С 3 аргументами – в первую очередь – это название крепежа, второй – список значений аргумента для крепежа, который станет request.param И, наконец, ключевое слово аргумент непрямо = Правда , что вызывает появление ценностей аргумента в request.param. .

Последнее, что нам нужно сделать, это добавить приспособление в качестве аргумента для тестирования, и мы сделаем.

Пропуск тестов

Есть ситуации, когда разумно пропустить некоторые тесты, будь то из-за окружающей среды ( Linux vs Windows ), подключение к Интернету, доступность ресурсов или других. Итак, как мы это делаем?

import pytest, os

@pytest.mark.skipif(os.system("service postgresql status") > 0,
                    reason="PostgreSQL service is not running")
def test_connect_to_database():
    ... # Run function that tries to connect to PostgreSQL database

Это показывает очень простой пример того, как вы можете пропустить действительный тест на основе некоторого состояния – в этом случае на основе того, работает ли сервер PostgreSQL на машине или нет. Есть много более крутых функций в Питиш связанные с пропуском или ожиданием неудач, и они очень хорошо документированы здесь Итак, я не буду здесь подробно поработать, так как кажется избыточным для копирования и вставки существующего контента здесь.

Заключение

Надеюсь, что эти немногие советы сделают ваш опыт тестирования немного более приятным и эффективным, и поэтому мотивируют вас писать еще несколько тестов, а затем. Если у вас есть какие-либо вопросы, не стесняйтесь добраться до меня, а также если у вас есть какие-либо предложения или трюки, пожалуйста, поделитесь их здесь. 🙂

Оригинал: “https://dev.to/martinheinz/pytest-features-that-you-need-in-your-testing-life-25cd”