В этой статье я покажу вам, как вы можете проверить веб-сервис Python, который был построен с использованием Connexion (Библиотека обертки вокруг колба). Мы перейдем за то, как вы можете издеваться над функциями и как вы можете проверить ваши конечные точки. Есть две связанные статьи, которые я написал в прошлом, перечисленном ниже. Во-первых, мы переходим, как создать веб-сервис с помощью Connexions, та же веб-сервис, которую мы будем в этой статье. Во второй статье я представляю, как вы можете использовать питиш-макет
и pteest-flask
Для проверки веб-службы Flask.
- Реализация простого отдыха API с использованием Openapi, Flask & Connexions
- Тестирование с Pтобыми-издевательствами и Pтойной колбой
Примерное приложение мы будем писать тесты, – это очень простой Crud API, управляющий зоомагазином. Это позволяет нам добавлять домашние животные, удалять домашние животные, обновлять домашние животные и запрашивать домашние животные в магазине.
Структура
Вы можете найти исходный код здесь. Наша структура проекта выглядит так:
. ├── openapi │ └── specification.yml ├── requirements.txt ├── test_api │ ├── core │ │ ├── __init__.py │ │ ├── pets.json │ │ └── pets.py │ ├── __init__.py │ ├── run.py │ └── web │ ├── controllers │ │ ├── __init__.py │ │ └── pets_controller.py │ ├── encoder.py │ ├── __init__.py │ ├── models │ │ ├── base_model_.py │ │ ├── __init__.py │ │ ├── pet.py │ │ └── pets.py │ └── util.py └── tests ├── conftest.py ├── __init__.py └── test_pets_controller.py
API.
Вот наш модуль контроллера под названием Веб/Контроллер/PETS_CONTROLLER.PY
Отказ Это где маршруты Connexion являются запросами:
import connexion import six from ..models.pet import Pet # noqa: E501 from ..models.pets import Pets # noqa: E501 from .. import util from test_api.core import pets def get_pet(pet_id): # noqa: E501 """Get a pet in the store # noqa: E501 :param pet_id: The id of the pet to retrieve :type pet_id: str :rtype: Pet """ try: pet = pets.get_pet(pet_id) response = Pet(id=pet.id, breed=pet.breed, name=pet.name, price=pet.price), 200 except KeyError: response = {}, 404 return response
Connexion использует спецификацию открытых API openapi/Спецификация.yml
, чтобы работать, какая функция для маршрута запросов на путь /pet/{pet_id}
Отказ Используется операция
рядом с X-Swagger-Router-Controller
Чтобы определить функцию, чтобы позвонить в PETS_CONTROLLER.PY
модуль.
/pet/{pet_id}: get: tags: - "pet" summary: "Get a pet in the store" operationId: "get_pet" parameters: - name: "pet_id" in: "path" description: "The id of the pet to retrieve" required: true type: "string" responses: 200: description: "Successfully retrived pet" schema: $ref: "#/definitions/Pet" 404: description: "Pet doesn't exist" x-swagger-router-controller: "test_api.web.controllers.pets_controller"
Тесты
Сейчас на наши тесты!
Библиотеки
pteest-flask Позволяет указывать приложение приложению, а затем отправлять запросы API с помощью этого приложения. Использование похоже на Запросы
Библиотека при отправке HTTP-запросов в наше приложение.
Pytest-Mock Простая обертка вокруг библиотеки моделей тестирования устройства, поэтому все, что вы можете сделать, используя Unittest.Mock
Вы можете сделать с Pytest-Mock
Отказ Основное отличие в использовании, вы можете получить доступ к нему, используя крепеж усадьба
Также макет заканчивается в конце теста. Принимая во внимание, что с обычной библиотекой издевательства, если вы говорите, макет Открыть ()
Функция, она будет издеваться за оставшуюся продолжительность этого тестового модуля, то есть оно повлияет на другие тесты.
conftest.py
Conftest.py
Файл автоматически работает PyTest и позволяет нашим тестовым модулям для доступа к приспособлениям, определенным в этом файле. Одна из лучших особенностей Pтойских приспособлений. Приспособление – это функции, которые имеют повторные использованные биты кода, мы можем запустить в наших модульных тестах, таких как статические данные, используемые тестами.
import os import json import pytest from test_api.run import create_app @pytest.fixture(scope="session") def app(): abs_file_path = os.path.abspath(os.path.dirname(__file__)) openapi_path = os.path.join(abs_file_path, "../", "openapi") os.environ["SPEC_PATH"] = openapi_path app = create_app() return app @pytest.fixture(scope="session", autouse=True) def clean_up(): yield default_pets = { "1": {"name": "ginger", "breed": "bengal", "price": 100}, "2": {"name": "sam", "breed": "husky", "price": 10}, "3": {"name": "guido", "breed": "python", "price": 518}, } abs_file_path = os.path.abspath(os.path.dirname(__file__)) json_path = os.path.join(abs_file_path, "../", "test_api", "core", "pets.json") with open(json_path, "w") as pet_store: json.dump(default_pets, pet_store, indent=4)
приложение()
В этом файле у нас есть две функции: приложение
Позволяет пользователям пройти клиент
Аргумент к другим тестам, а затем мы можем проверить ваше веб-приложение. Вы можете получить больше информации здесь О том, как приложения для колб могут быть проверены. По сути, нам не нужно начать/остановить сервер до/после наших тестов.
Давая это Scope = Session
Смесь будет создан один раз, прежде чем все наши тесты выполняются. Наше Run.py
Файл выглядит так:
import os import connexion from .web import encoder def create_app(): if "SPEC_PATH" in os.environ: openapi_path = os.environ["SPEC_PATH"] else: abs_file_path = os.path.abspath(os.path.dirname(__file__)) openapi_path = os.path.join(abs_file_path, "../", "../", "openapi") app = connexion.FlaskApp( __name__, specification_dir=openapi_path, options={"swagger_ui": False, "serve_spec": False}, ) app.add_api("specification.yml", strict_validation=True) flask_app = app.app flask_app.json_encoder = encoder.JSONEncoder return flask_app
create_app
Функция создает наше веб-приложение и возвращает объект Flask. Помните, что библиотека Connexion – это просто обертка вокруг колбы. Connexion просто уменьшает код котельной, который мы написали. Опять же, вы можете прочитать статью выше, чтобы получить более подробную информацию о том, как она работает.
clean_up ()
@pytest.fixture(scope="session", autouse=True) def clean_up(): yield default_pets = { "1": {"name": "ginger", "breed": "bengal", "price": 100}, "2": {"name": "sam", "breed": "husky", "price": 10}, "3": {"name": "guido", "breed": "python", "price": 518}, } abs_file_path = os.path.abspath(os.path.dirname(__file__)) json_path = os.path.join(abs_file_path, "../", "test_api", "core", "pets.json") with open(json_path, "w") as pet_store: json.dump(default_pets, pet_store, indent=4)
Второй прибор, который мы определяем, называется Clean_up
из-за доходность
Линия, эта функция будет работать после завершения всех наших испытаний. доходность
Команда связана с генераторами, вы можете прочитать Больше здесь Отказ В нашем случае он используется в приборах Ptyest, чтобы мы могли запускать некоторые задания очистки после завершения нашего теста. В этом примере я просто заменяю содержимое файла JSON, который действует как хранилище данных (например, в базе данных), к его значениям по умолчанию, до того, как тест был запущен.
Поскольку Pteest-3.0, приспособления с использованием нормального декоратора прибора могут использовать оператор выхода для обеспечения значений прибора и выполнить код разрыва – Pteest Docs
test_pets_controller.py
Теперь мы перешли на установку, необходимые для наших тестов, давайте посмотрим, как мы можем проверить наш код. Так что наш первый тест выглядит как:
def test_get_all_pets(client): url = "/api/v1/pet" expected_json = [ {"id": "1", "name": "ginger", "breed": "bengal", "price": 100}, {"id": "2", "name": "sam", "breed": "husky", "price": 10}, {"id": "3", "name": "guido", "breed": "python", "price": 518}, ] response = client.get(url) assert response.json == expected_json
Это очень простой тест, здесь мы используем приложение
Приспособление, которое мы определены выше. Это клиент
Крепеж можно использовать, потому что мы используем pteest-flask
библиотека. Как вы можете видеть, это выглядит очень похоже на Запросы
, где мы даем это путь /API/V1/Pet
а затем скажи, что за просьба сделать Client.get
Отказ В то время как синтаксис между Запросы
Библиотека и клиент
Приспособление практически идентична. Одна большая разница, которая всегда, похоже, отключается, в Запросы
Чтобы получить данные JSON из ответ
объект будет Ответ.json ()
I.e Это функция. Однако в клиент
( Pytest-Flask
) Приспособление Доберите данные JSON, которые мы делаем Ответ. Джусон
который является просто атрибутом объекта, а не функции.
Сам тест очень прост, он приносит просьбу, чтобы получить все домашние животные в зоомагазине. Затем мы сравним, что мы ожидаем быть в зоомагазине Assert Response.json.
.
Следующий тест, который у нас выглядит так:
@pytest.mark.parametrize( "pet_data, expected_status, expected_data", [ ({"name": "Yolo", "breed": "shorthair", "price": 100}, 201, {"id": 4}), ({}, 400, {}), ({"a": "b"}, 400, {}), ] ) def test_add_a_pet(client, pet_data, expected_status, expected_data): url = "/api/v1/pet" response = client.post(url, json=pet_data) assert response.status_code == expected_status if response.status_code == 200: assert response.json == expected_data
Этот тест пытается добавить новый питомец в магазин. Это похоже на другой тест, мы все еще используем клиент
приспособление, чтобы сделать запрос. На этот раз мы также дам это немного JSON
данные, следовательно, мы предоставляем JSON
аргумент json = Pet_data
Это автоматически устанавливает заголовки правильно, поэтому сервер знает, что он получает данные JSON.
Мы также используем украсьте @ pytest.mark.parametrize.
. Это позволяет нам запустить наши тесты против списка данных. Поэтому нам не нужно писать тот же тест х количество раз. Мы просто передаем тестовые разные аргументы. Pтойцы запускают этот тест X номер раз один раз для каждого элемента в списке. Так, например, первый раз, когда тестовые прогоны:
pet_data = {"name": "Yolo", "breed": "shorthair", "price": 100} expected_status = 200 expected_data = {"id": 4}
Второе, как это:
pet_data = {} expected_status = 200 expected_data = {}
И так далее, и так далее. Это помогает сохранить наш тестовый файл меньше и сохраняет сухое (не повторять себя). Очень хорошая особенность Pтойца и один, который я использую сильно.
Последний тест, который у нас в этом файле выглядит:
def test_add_pet_fail_json(client, mocker): pet_data = {"name": "Yolo", "breed": "shorthair", "price": 100} url = "/api/v1/pet" mock = mocker.post("connexion.request") mock.is_json = False response = client.post(url, json=pet_data) assert response.status_code == 400
Наконец, мы видим Pytest-Mock
используется через усадьба
Приспособление мы автоматически получаем доступ к. усадьба
просто простая обертка вокруг Unittest.Mock
модуль. Основное отличие, будучи издевательства на существующих на протяжении всего теста. Издевание часто используется, когда тестирование единиц, и мы не можем полагаться на внешние зависимости, такие как соединения с базой данных или другой веб-сервис.
def add_pet(body): # noqa: E501 # ... if connexion.request.is_json: body = Pet.from_dict(connexion.request.get_json()) # noqa: E501 # ...
В этом примере мы хотим издеваться со стороны Connexion, которая проверяет, является ли отправляемые данные действительными JSON. Мы хотим connexion.request.is_json
вернуть Ложь
мы можем сделать это так:
mock = mocker.patch("connexion.request") mock.is_json = False
С IS_JSON
это атрибут connexion.request
Модуль и не функция, которую нам нужно установить ложь на другой строке. Если IS_JSON
была функция, которую мы хотели вернуть Ложь
Мы могли бы сделать mocker.patch ("connexion.request.is_json")
вместо.
Вы можете запустить тесты локально, запустив pteest
Команда или если вы хотите запустить код в этой статье, вы можете сделать следующее:
gcl https://gitlab.com/hmajid2301/articles.git cd 27.\ Mocking\ in\ Flask\ with\ Pytest/source_code virtualenv .venv source .venv/bin/activate pip install -r requirements.txt pytest
Вот и все, примеры выше охватывают большую часть вещей, которые вам нужно будет издеваться и проверить вашу веб-сервис CONNEXION.
Информация: pteest-flask
Обеспечивает целую кучу других функций, которые могут быть полезны, вы можете найти полный список здесь
Приложение
Оригинал: “https://dev.to/hmajid2301/testing-mocking-a-connexion-flask-application-with-pytest-39fd”