Spec-Zone.ru › Flask

Тестирование приложений Flask

Flask предоставляет утилиты для тестирования приложения. Данная документация описывает методы работы с различными частями приложения в тестах.

Мы будем использовать фреймворк pytest для настройки и запуска наших тестов.

$ pip install pytest

В учебнике описывается, как написать тесты для 100% покрытия примера приложения Flaskr blog. Смотрите учебник по тестам для подробного объяснения конкретных тестов для приложения.

Идентификация тестов

Тесты обычно располагаются в папке tests. Тесты представляют собой функции, начинающиеся с test_, в Python-модулях, начинающихся с test_. Тесты также могут быть сгруппированы в классах, начинающихся с Test.

Иногда сложно понять, что следует тестировать. В целом, старайтесь тестировать написанный вами код, а не код библиотек, которые вы используете, поскольку они уже протестированы. Попробуйте выделить сложные поведения в отдельные функции для индивидуального тестирования.

Фикстуры

Pytest фикстуры позволяют писать код, который можно повторно использовать в тестах. Простая фикстура возвращает значение, но фикстура также может выполнять настройку, возвращать значение, а затем выполнять разборку. Фикстуры для приложения, клиента теста и исполнителя CLI показаны ниже, их можно разместить в tests/conftest.py.

Если вы используете фабрику приложения, определите фикстуру app для создания и настройки экземпляра приложения. Вы можете добавить код до и после yield для настройки и разборки других ресурсов, таких как создание и очистка базы данных.

Если вы не используете фабрику, у вас уже есть объект приложения, который можно импортировать и настроить напрямую. Вы по-прежнему можете использовать фикстуру app для настройки и разборки ресурсов.

import pytest
from my_project import create_app

@pytest.fixture()
def app():
    app = create_app()
    app.config.update({
        "TESTING": True,
    })

    # other setup can go here

    yield app

    # clean up / reset resources here


@pytest.fixture()
def client(app):
    return app.test_client()


@pytest.fixture()
def runner(app):
    return app.test_cli_runner()

Отправка запросов с помощью клиента теста

Клиент теста отправляет запросы к приложению без запуска реального сервера. Клиент Flask расширяет клиент Werkzeug, см. эти документы для дополнительной информации.

У client есть методы, соответствующие общим методам HTTP-запросов, таким как client.get() и client.post(). Они принимают множество аргументов для построения запроса; полную документацию можно найти в EnvironBuilder. Обычно вы будете использовать path, query_string, headers, и data или json.

Для отправки запроса вызовите метод, который должен использовать запрос, с путем к маршруту для тестирования. Возвращается TestResponse для проверки данных ответа. Он имеет все обычные свойства объекта ответа. Обычно вы будете обращаться к response.data, который представляет собой байты, возвращенные представлением. Если вы хотите использовать текст, Werkzeug 2.1 предоставляет response.text, или используйте response.get_data(as_text=True).

def test_request_example(client):
    response = client.get("/posts")
    assert b"<h2>Hello, World!</h2>" in response.data

Передайте словарь query_string={"key": "value", ...} для задания аргументов в строке запроса (после ? в URL). Передайте словарь headers={} для задания заголовков запроса.

Для отправки тела запроса в запросах POST или PUT передайте значение в data. Если переданы сырые байты, используется именно это тело. Обычно вы передадите словарь для задания данных формы.

Данные формы

Для отправки данных формы передайте словарь в data. Заголовок Content-Type будет автоматически установлен на multipart/form-data или application/x-www-form-urlencoded.

Если значение представляет собой объект файла, открытый для чтения байтов (режим "rb"), он будет обрабатываться как загруженный файл. Чтобы изменить обнаруженное имя файла и тип содержимого, передайте кортеж (file, filename, content_type). Объекты файлов будут закрыты после отправки запроса, поэтому им не нужно использовать обычный шаблон with open() as f:.

Полезно хранить файлы в папке tests/resources, а затем использовать pathlib.Path для получения файлов относительно текущего тестового файла.

from pathlib import Path

# get the resources folder in the tests folder
resources = Path(__file__).parent / "resources"

def test_edit_user(client):
    response = client.post("/user/2/edit", data={
        "name": "Flask",
        "theme": "dark",
        "picture": (resources / "picture.png").open("rb"),
    })
    assert response.status_code == 200

Данные JSON

Для отправки данных JSON передайте объект в json. Заголовок Content-Type будет автоматически установлен на application/json.

Аналогично, если ответ содержит данные JSON, атрибут response.json будет содержать десериализованный объект.

def test_json_data(client):
    response = client.post("/graphql", json={
        "query": """
            query User($id: String!) {
                user(id: $id) {
                    name
                    theme
                    picture_url
                }
            }
        """,
        variables={"id": 2},
    })
    assert response.json["data"]["user"]["name"] == "Flask"

Следование перенаправлениям

По умолчанию клиент не выполняет дополнительные запросы, если ответ является перенаправлением. Передав follow_redirects=True в метод запроса, клиент будет продолжать выполнять запросы до тех пор, пока не будет возвращен ответ, не являющийся перенаправлением.

TestResponse.history представляет собой кортеж ответов, которые привели к конечному ответу. Каждый ответ имеет атрибут request, который записывает запрос, который произвел этот ответ.

def test_logout_redirect(client):
    response = client.get("/logout", follow_redirects=True)
    # Check that there was one redirect response.
    assert len(response.history) == 1
    # Check that the second request was to the index page.
    assert response.request.path == "/index"

Доступ к сессии и ее изменение

Для доступа к переменным контекста Flask, в основном к session, используйте клиента в операторе with. Контекст приложения и запроса останутся активными *после* отправки запроса, до тех пор, пока блок with не завершится.

from flask import session

def test_access_session(client):
    with client:
        client.post("/auth/login", data={"username": "flask"})
        # session is still accessible
        assert session["user_id"] == 1

    # session is no longer accessible

Если вы хотите получить доступ к значению в сессии или установить его *до* отправки запроса, используйте метод клиента session_transaction() в операторе with. Он возвращает объект сессии и сохранит сессию по завершении блока.

from flask import session

def test_modify_session(client):
    with client.session_transaction() as session:
        # set a user id without going through the login route
        session["user_id"] = 1

    # session is saved now

    response = client.get("/users/me")
    assert response.json["username"] == "flask"

Запуск команд с помощью исполнителя CLI

Flask предоставляет test_cli_runner() для создания FlaskCliRunner, который запускает команды CLI изолированно и собирает вывод в объекте Result. Исполнитель Flask расширяет исполнитель Click, см. эти документы для дополнительной информации.

Используйте метод исполнителя invoke() для вызова команд так же, как они вызывались бы с помощью команды flask из командной строки.

import click

@app.cli.command("hello")
@click.option("--name", default="World")
def hello_command(name):
    click.echo(f"Hello, {name}!")

def test_hello_command(runner):
    result = runner.invoke(args="hello")
    assert "World" in result.output

    result = runner.invoke(args=["hello", "--name", "Flask"])
    assert "Flask" in result.output

Тесты, зависящие от активного контекста

У вас могут быть функции, вызываемые из представлений или команд, которые ожидают активного контекста приложения или контекста запроса, поскольку они обращаются к request, session, или current_app. Вместо тестирования их путем отправки запроса или вызова команды вы можете создать и активировать контекст напрямую.

Используйте with app.app_context() для помещения контекста приложения. Например, расширения баз данных обычно требуют активного контекста приложения для выполнения запросов.

def test_db_post_model(app):
    with app.app_context():
        post = db.session.query(Post).get(1)

Используйте with app.test_request_context() для помещения контекста запроса. Он принимает те же аргументы, что и методы запроса клиента теста.

def test_validate_user_edit(app):
    with app.test_request_context(
        "/user/2/edit", method="POST", data={"name": ""}
    ):
        # call a function that accesses `request`
        messages = validate_edit_user()

    assert messages["name"][0] == "Name cannot be empty."

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

def test_auth_token(app):
    with app.test_request_context("/user/2/edit", headers={"X-Auth-Token": "1"}):
        app.preprocess_request()
        assert g.user.name == "Flask"

© 2010 Pallets
Licensed under the BSD 3-clause License.
https://flask.palletsprojects.com/en/stable/testing/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API