Автор оригинала: Max Ong Zong Bao.
Сегодня в нашей заключительной части серии из 3 частей я расскажу о создании реальных REST API с помощью PyTest.
Для тех, кто новичок в этой серии, вы можете посмотреть на часть 1 , чтобы понять различные инструменты, которые я буду использовать для создания конечных точек REST API менеджера расходов.
Кроме того, посмотрите на часть 2 в издевательстве над конечными точками API для прототипирования ваших проектов API.
Инструмент
исходный код
Конечные Точки Для Создания
Теперь в последней части серии мы рассмотрим функции CRUD , которые используют только метод HTTP-запроса GET .
Поскольку создание PUT , POST и DELETE конечных точек идентично, было бы лучше ввести использование Pytest .
Конечные точки и документация API Заключительная часть:
Документация API в Postman
- Получить список транзакций – GET
- Создать новую транзакцию – POST
- Обновление отдельной транзакции – PUT
- Удалить отдельную транзакцию – УДАЛИТЬ
Настройка проекта
Создание папки проекта
Создайте свой проект в Linux , создав папку с именем expenses_manager .
Далее в разделе создайте виртуальную среду с помощью pipe и переключитесь на виртуальную среду python.
pipenv install pipenv shell
Установка пакетов Python
Теперь вам нужно установить следующее, набрав команду ниже.
pipenv shell pipenv install Flask flask-cors pytest pytest-cov pytest-flask requests pylint
Установив базовые пакеты python, мы приступим к созданию нашего первого приложения Flask.
Создание Вашего Первого приложения Flask
Создайте файл с именем expenses_manager.py , затем откройте IDE или выбранный редактор, чтобы внести изменения в содержимое файла.
Создайте файл с именем || expenses_manager.py || , затем откройте IDE или выбранный редактор, чтобы внести изменения в содержимое файла.
from flask import Flask # Import the flask web server app = Flask( __name__ ) # Single module that grabs all modules executing from this file @app.route('/') # Tells the flask server on which url path does it trigger which for this example is the index page calling "hello_world" function. def hello_world(): return 'Hello, World!'
Выполнение приложения Flask
Теперь, как только вы добавили код в файл, нам нужно выполнить следующую команду start this flask app:
export FLASK_APP=expenses_manager.py flask run
Поздравляю вы только что создали свое первое приложение flask. Вы можете открыть свой браузер и ввести в него следующий URL-адрес “127.0.0.1:5000”. Для отмены вам нужно нажать ctrl + c , чтобы выйти из сервера.
Режим Разработки В Колбе
Введите следующую команду, чтобы мы включили режим development .
В этом режиме ваше приложение flask имеет отладчик и автоматически перезапускается самостоятельно всякий раз, когда происходит изменение кода.
export FLASK_ENV=development flask run
Понимание маршрутизации
Маршрутизация-это способ, которым вы назначаете определенную веб-страницу для загрузки. Это может быть простой пример, как показано ниже:
@app.route('/') # Routes you to the index page def index(): return 'Index Page' @app.route('/hello') # Routes you to the page with http://127.0.0.1:5000/hello/ def hello(): return 'Hello, World' @app.route('/projects/') # URL with trailing slash def projects(): return 'The project page' @app.route('/about') # URL without a trailing slash def about(): return 'The about page'
Это может быть очень просто или очень сложно, я не буду много рассказывать, так как мы сосредоточены на построении функции GET менеджера расходов .
Я прикреплю справочную ссылку для вас к разделу ссылок для лучшего понимания маршрутизации.
Обратите внимание, что по умолчанию если вы не включаете URL-адрес с завершающей косой чертой , который является этим / .
Колба будет автоматически перенаправлять вас 404 когда вы добавляете страницу с / в конце URL-адреса.
Создание Первой Конечной Точки REST API
Если вы не читали более ранние части серии, пожалуйста, перейдите к части 2 или 3 серии, чтобы понять, что такое HTTP-методы и код состояния .
Вы можете посмотреть на приведенный ниже пример, изменив свой expenses_manager.py код к следующему, чтобы понять, как это работает.
from flask import Flask, request, jsonify # Imports the flask library modules app = Flask( __name__ ) # Single module that grabs all modules executing from this file @app.route('/login', methods=['GET', 'POST']) # HTTP request methods namely "GET" or "POST" def login(): data = [] if request.method == 'POST': # Checks if it's a POST request data = [dict(id='1', name='max', email='max@gmail.com')] # Data structure of JSON format response = jsonify(data) # Converts your data strcuture into JSON format response.status_code = 202 # Provides a response status code of 202 which is "Accepted" return response # Returns the HTTP response else: data = [dict(id='none', name='none', enmail='none')] # Data structure of JSON format response = jsonify(data) # Converts your data strcuture into JSON format response.status_code = 406 # Provides a response status code of 406 which is "Not Acceptable" return response # Returns the HTTP response
Теперь вы можете открыть свой Postman , чтобы создать запрос под названием Testing Login Request .
Введите этот URL-адрес http://127.0.0.1:5000/login чтобы ваш запрос и setHTTPmethod были либо GET , либо POST request, затем нажмите send , чтобы получить результат ответа.
Создание скрипта Bash для настройки параметров конфигурации
Мы создадим скрипт bash , который позволит выполнять вышеперечисленные настройки без постоянного ввода команды.
Создайте файл, который называется env.sh и заполните содержимое файла приведенным ниже кодом.
Создайте файл, который называется || env.sh || и заполните содержимое файла приведенным ниже кодом.
#!/usr/bin/env bash pipenv shell export FLASK_ENV=development FLASK_APP=expenses_manager.py flask run
После того, как вы создали файл с именем env.sh , вам нужно изменить разрешение файла с помощью этой команды:
Чтобы запустить скрипт bash, вам нужно находиться в папке project root , используя приведенную ниже команду.
Создание вашего первого тестового случая
Теперь мы начнем с создания нашего первого тестового случая для вашей конечной точки flask API.
Мы проверим, работает ли приложение flask, проверив, служит ли index.page HTTP-ответом.
Создание test_endpoints.py
Итак, давайте создадим тестовый скрипт вызова test_endpoints.py и добавьте следующий код в только что созданный файл:
Итак, давайте создадим тестовый скрипт вызова || test_endpoints.py || и добавьте следующий код в только что созданный файл:
import pytest import requests url = 'http://127.0.0.1:5000' # The root url of the flask app def test_index_page(): r = requests.get(url+'/') # Assumses that it has a path of "/" assert r.status_code == 200 # Assumes that it will return a 200 response
Выполнение test_endpoints.py
Пусть приложение flask работает в первом терминале. Затем создайте второй терминал и запустите pytest , используя приведенную ниже команду:
Вы видели F рядом с вашим test_endpoints.py это означает, что ваш тестовый случай провалился .
Пожалуйста, не беспокойтесь, так как это ожидаемое поведение с нашей первой попытки, так как мы не включили конечную точку индекса , которая возвращает ответ .
Чтобы исправить свой тестовый сценарий, вы можете следовать предложению pytest, изменив код состояния на 400 вместо этого 200 и выполните тест еще раз.
Тест действительно проходит с “.” но это не сможет проверить , если ваше приложение flask работает под корневой папкой и возвращает HTTP 200 ответ.
Поэтому нам нужно создать конечную точку индекса вместо того, чтобы исправлять наши тестовые случаи.
Создайте конечную точку индекса
Поскольку тестовый случай в test_index_page требует конечной точки с путем /| и HTTP-ответом 200 .
Мы добавим индексную страницу в наш expenses_manager.py :
Мы добавим индексную страницу в наш || expenses_manager.py ||:
@app.route('/', methods=['GET']) def index_page(): response = jsonify('Hello World!!!') response.status_code = 200 return response
Теперь введите pytest -v в 2-й терминал в то время как ваш 1-й терминал выполняет приложение flask.
Команда pytest-v предоставляет вам дополнительную информацию о ваших тестовых случаях, которая также полезна для отладки ваших тестовых случаев и конечных точек python.
Теперь после выполнения предыдущей команды вы должны увидеть, что ваши тестовые случаи проходят. Потрясающая работа!!!! При создании вашей первой конечной точки и тестового случая.
Давайте сделаем заслуженный отдых и перейдем к следующему разделу, когда вы будете готовы погрузиться дальше.
Создание конечных точек Менеджера расходов
С тех пор как вы начали создавать свою первую конечную точку в Flask & test cases с помощью Pytest .
Давайте рассмотрим, как составляется список API, необходимых для создания менеджера расходов:
Список API для создания:
- Получить список транзакций – GET
- Создать новую транзакцию – POST
- Обновление отдельной транзакции – PUT
- Удалить отдельную транзакцию – УДАЛИТЬ
Получить список транзакций
Теперь начните с первой конечной точки GET , которая предоставляет список транзакций.
Что такое минимальный тест , который нам нужно создать, чтобы проверить, работает ли он?
Создание Пользовательских Историй
Пользовательские истории полезны для того, чтобы выяснить, что нужно создать перед реализацией.
Мы возьмем этот сценарий user story script для создания пользовательского тестового случая:
As (role of the user), I want to (the activity) so that (desired result)
Таким образом, при создании пользовательской истории это будет выглядеть примерно так
Как Пользователь , я хочу иметь снимок моих расходов , чтобы я знал, куда я трачу свои деньги
Создание Тестового случая для Баланса В Менеджере расходов
С помощью этой пользовательской истории мы можем построить минимальный тестовый случай. Который должен показать баланс , который у нас есть на вашем счете.
С помощью этой || пользовательской истории || мы можем построить минимальный тестовый случай. Который должен показать || баланс||, который у нас есть на вашем счете.
def test_get_balance_in_transacations(): r = requests.get(url+'/transactions/') assert r.status_code == 200
Теперь давайте запустим ваш недавно созданный test_get_balance_in_transactions тестовый случай.
Поскольку вы не создали конечную точку transactions , она не может попросить вас изменить тестовый случай на 404 .
Чтобы он прошел, нам нужно вызвать конечную точку под названием transactions in expenses_manager.py .
Чтобы он прошел, нам нужно вызвать конечную точку под названием || transactions || in || expenses_manager.py || .
@app.route('/transactions/', methods=['GET']) def list_of_transactions(): response = jsonify({}) response.status_code = 200 return response
С этим вы должны были пройти свое первое испытание. Теперь давайте создадим еще один тестовый случай, который проверяет содержимое ответа на наличие баланса 0 .
С этим вы должны были пройти свое первое испытание. Теперь давайте создадим еще один тестовый случай, который проверяет содержимое ответа на наличие || баланса |||| 0 || .
def test_get_balance_in_transacations(): r = requests.get(url+'/transactions/') assert r.status_code == 200 data = r.json() assert data[balance'] == 0
Ожидайте, что ваши тестовые случаи потерпят неудачу, если вы запустите приведенный выше код. Нам нужно изменить expenses_manager.py чтобы это прошло.
Ожидайте, что ваши тестовые случаи потерпят неудачу, если вы запустите приведенный выше код. Нам нужно изменить || expenses_manager.py || чтобы это прошло.
@app.route('/transactions/', methods=['GET']) def list_of_transactions(): response = jsonify({'balance': 0}) response.status_code = 200 return response
Когда вы запустите его снова, вы увидите, что он проходит для этого тестового случая.
Вы завершили Историю пользователя?
Итак, теперь мой вопрос к вам: выполнили ли мы нашу историю пользователя для этого тестового случая?
Если нет, то чего еще не хватает? Хорошей догадкой будут фиктивные конечные точки, которые вы создали в part 2 .
Поскольку мы знаем, что помимо баланса менеджера расходов, нам нужно получить количество транзакций , основанных в конечной точке.
Перед этим нам нужно очистить наш код для этой test_get_balance_in_transactions конечной точки.
Перед этим нам нужно очистить наш код для этой || test_get_balance_in_transactions || конечной точки.
def test_get_balance_in_transacations(): r = requests.get(url+'/transactions/') data = r.json() assert r.status_code == 200 assert data['balance'] == 0
Получить количество транзакций в конечной точке Транзакций
Как только вы закончите с этим кодом, создайте новый тестовый случай, который проверяет количество транзакций в конечной точке Transactions .
Как только вы закончите с этим кодом, создайте новый тестовый случай, который проверяет количество транзакций в конечной точке || Transactions||.
def test_get_number_of_transacations(): r = requests.get(url+'/transactions/') data = r.json() assert r.status_code == 200 assert len(data['transactions']) != 0
Этот тестовый случай проверяет общее количество транзакций должно быть больше, чем 0 .
В очередной раз ваш тестовый случай | терпит неудачу поэтому давайте создадим транзакцию для тестового случая, чтобы пройти .
В очередной раз ваш || тестовый случай || | терпит неудачу || поэтому давайте создадим транзакцию для тестового случая, чтобы || пройти || .
@app.route('/transactions/', methods=['GET']) def list_of_transactions(): response = jsonify({'balance': 0, 'transactions': [ {} ]}) response.status_code = 200 return response
Проверка полей отдельной транзакции
Как и вы до сих пор, мы только проверили, имеет ли он больше, чем 0 сделки.
Мы не смогли проверить, имеют ли транзакции те же поля, что и фиктивные конечные точки , найденные в части 2 серии.
Итак, теперь давайте создадим эти тесты для проверки полей внутри транзакций.
Итак, теперь давайте создадим эти тесты для проверки полей внутри транзакций.
def test_individual_transaction_fields(): r = requests.get(url+'/transactions/') data = r.json() fields = list(data['transactions']) assert r.status_code == 200 assert fields[0]['amount'] >= 0.00 assert fields[0]['current_balance'] < 240 assert 'jean' in fields[0]['description'] assert 0 < fields[0]['id'] assert 300 == fields[0]['inital_balance'] assert "2019-01-12 09:00:00" == fields[0]['time'] assert fields[0]['type'] != 'income'
Опять же, когда вы запускаете это изначально, это не удается, так что теперь самое время добавить в поля отдельные транзакции.
Опять же, когда вы запускаете это изначально, это не удается, так что теперь самое время добавить в поля отдельные транзакции.
@app.route('/transactions/', methods=['GET']) def list_of_transactions(): response = jsonify({'balance': 0, 'transactions': [ {'amount': 0.0, 'current_balance': 230, 'description': 'blue jean', 'id':2, 'inital_balance': 300, 'time': "2019-01-12 09:00:00", 'type': 'expense'} ]}) response.status_code = 200 return response
Рефакторинг В Один Класс
Теперь мы проведем рефакторинг вашего 4 тестовые случаи и консолидировать его в один класс для удобства выполнения тестов, которые мы называем test suite .
Который вы можете ввести эту команду для тестирования этого конкретного набора тестов
pytest -v test_endpoints.py::NameOfTheSuite
Который вы можете ввести эту команду для тестирования этого конкретного набора тестов ||
class TestTransactions(): def test_index_page(self): r = requests.get(url+'/') assert r.status_code == 200 def test_get_balance_in_transacations(self): r = requests.get(url+'/transactions/') data = r.json() assert r.status_code == 200 assert data['balance'] == 0 def test_get_number_of_transacations(self): r = requests.get(url+'/transactions/') data = r.json() assert r.status_code == 200 assert len(data['transactions']) != 0 def test_individual_transaction_fields(self): r = requests.get(url+'/transactions/') data = r.json() fields = list(data['transactions']) assert r.status_code == 200 assert fields[0]['amount'] >= 0.00 assert fields[0]['current_balance'] < 240 assert 'jean' in fields[0]['description'] assert 0 < fields[0]['id'] assert 300 == fields[0]['inital_balance'] assert "2019-01-12 09:00:00" == fields[0]['time'] assert fields[0]['type'] != 'income'
Основы тестовой разработки
Если вы не заметили, что постоянный сбой и рефакторинг для прохождения ваших тестовых случаев-это практика разработки программного обеспечения, называемая Test Driven Development .
Который может быть использован в дополнение к нему с помощью Pytest для создания различных тестовых случаев для ваших будущих проектов.
Основной процесс TDD
- Создайте неудачный тестовый случай
- Реализовать код передать
- Рефакторинг кода и снова начните с самого верха
Оставшиеся истории конечных точек
Вот оставшиеся конечные точки пользовательские истории для вас, чтобы создать которые я предоставлю исходный код через две недели в моем репо GitHub для этой сопутствующей серии учебников:
Создайте новую транзакцию – Как Пользователь , я хочу иметь запись моих расходов так, чтобы Я знал, куда идут мои деньги
Обновить отдельную транзакцию – Как Пользователь , я хочу отредактировать конкретную транзакцию так, чтобы ** У меня был правильный баланс**
Удалить отдельную транзакцию – Как Пользователь , я хочу удалить транзакцию так, чтобы ** У меня был правильный баланс в моем менеджере расходов**
Вывод
Если бы вы были со мной до самого конца этой серии, я хотел бы поблагодарить вас за то, что вы нашли время, чтобы пройти через эту серию из 3 частей, поскольку написать ее-это не просто подвиг.
Помните, что я выпущу решение для этой серии оставшихся конечных точек для POST , PUT & DELETE HTTP-запросов примерно через две недели .
Для меня было честью написать это 3 часть серии, я надеюсь, что то, что вы узнали, может быть полезно вам в создании RESTful API конечных точек в Flask с использованием PyTest , Postman и TDD методов.
Если вам понравилась моя статья, пожалуйста подпишитесь на рассылку Max Adventurer’s Newsletter для потрясающего контента, на который я натыкаюсь еженедельно в Python , Startup и Web Development .
Вы также можете следовать за мной, чтобы получить последнее обновление моей статьи о CodeMentor
Этот пост был первоначально опубликован в блоге Макса по адресу Building Restful API with Flask, Postman & PyTest – Part 3 (Время чтения: 20 минут) и фото из Фото Рейчел Горжестани на Unsplash