Реализация проекта
Flask —
это микрофреймворк для создания веб-приложений на Python, который ценится за лёгкость, гибкость и минимализм.
Зачем он нужен?
Когда нужно быстро «заглушить» метод, Flask отлично справляется с этой задачей.
По времени это сопоставимо с использованием WireMock Standalone, но с одним важным плюсом: в Flask можно легко реализовать ту логику, которая в WireMock невозможна (или слишком сложна), благодаря встроенным возможностям Python и его библиотекам.
Flask — это хорошее решение, если:
Почему Flask — удобный инструмент для создания заглушек
Начало работы
Установите Python
Установите Flask через pip
Выберите IDE на свой вкус
Создайте рабочую папку с .ру файлом внутри
Содержание requirements.txt
Werkzeug==2.3.7
Flask==2.2.2
gunicorn==23.0.0
gevent==25.5.1
Содержание Dockerfile
FROM python:3.9
WORKDIR /backend
COPY requirements.txt /backend
RUN pip3 install --upgrade pip -r requirements.txt
COPY . /backend
EXPOSE 5000
ENTRYPOINT gunicorn --workers 9 --worker-class gevent -b 0.0.0.0:5000 app:app

testmock.py
from flask import Flask, jsonify, request
import time, json, xml.etree.ElementTree as ET
app = Flask(__name__)
@app.route('/hello/world', methods=['GET'])
def test1():
time.sleep(0.1)
return ("hello")
if __name__ == '__main__':
app.run(host='0.0.0.0')
Каждый рест в заглушке — отдельный @app.route. Для эмулирования задержки используется time_sleep() (подается значение в секундах, 0.1 — это 100ms).
Для каждого реста должно быть уникальное название для вызываемого при запросе на сервер метода (тут test1()).
В methods можно написать несколько методов одновременно.
Запускаем python testmock.py, по умолчанию сервер отвечает по localhost:5000, проверяем командной строкой — curl localhost:5000/hello/world.
P.S.: большинство подключенных библиотек для такого реста, конечно же, не нужны — понадобятся позже.
P.P.S.: в реальном тестировании лучше запускать заглушку вот так:
gunicorn —workers * —worker-class gevent -b 0.0.0.0:5000 app:app
* — заменить на 2 * (кол-во CPU под заглушку) + 1
JSON, работа с файлами, корреляция
Техническое задание
Нужно заглушить метод /api/token, который будет принимать POST-запросы и возвращать заранее заданный JSON-ответ (например, из таблицы) со статусом 201.
Запрос
POST
/api/token
{
"id": {произвольный id}
}
Ответ
{
"access_token": "token",
"expires_in": 1800,
"respId": {id из запроса}
}
201
Создаём файл testresponse.json с шаблоном ответа.
Можно просто взять JSON из технического задания и либо удалить поле respId, либо присвоить ему любое значение.
Пример содержимого:
testresponse.json
{
"access_token": "token",
"expires_in": 1800,
"respId": null
}
Перед REST-методами добавляем чтение файла в понятный Python JSON-объект response.
Чтение файла
with open('testresponse.json', 'r') as file:
response1 = json.load(file)
Далее добавляем наш рест
/api/token
@app.route('/api/token', methods=['POST'])
def test2():
requestBody = request.get_json(force=True)
response1['respId'] = requestBody['id']
time.sleep(0.1)
return response1, 201
request.get_json(force=True)преобразует тело запроса в JSON-объект
response1[‘respId’] = requestBody[‘id’] вычитает значение поля id из запроса и присвоит его полю respId объекта response1, который вернется как ответ клиенту
Начинаем проверку

Имея базовые знания в Python, в заглушку можно добавить простую логику — например, возвращать разные коды страны в зависимости от первой цифры id из запроса.
Логика обработки
if str(requestBody['id'])[0]=='8':
response1['ccode']='RU'
else: response1['ccode']='EU'
XML, работа с заголовками и параметрами из запроса
Техническое задание
Нам нужно заглушить метод /api/postbalance, который будет отвечать на POST-запросы ответом из таблицы
Запрос
POST
/api/postbalance?id={произвольный id}
<balance>
<kpp>{произвольный kpp}</kpp>
</balance>
headers:
inn - {произвольный inn}
Ответ
<response>
<inn>{inn из заголовка запроса}</inn>
<kpp>{kpp из тела запроса}</kpp>
<status>RECIEVED</status>
</response>
headers:
customer-id - {id из параметра запроса}
<response>
<inn>null</inn>
<kpp>null</kpp>
<status>RECIEVED</status>
</response>
ET
response2 = ET.parse('xmltestresponse.xml').getroot()
/api/postbalance
@app.route('/api/postbalance', methods=['POST'])
def test3():
requestBody = ET.fromstring(request.data)
response2.find('kpp').text=requestBody.findtext('kpp')
response2.find('inn').text=request.headers.get('inn')
time.sleep(0.1)
return ET.tostring(response2), {"customer-id": request.args.get('id'), "Content-Type": "application/xml"}
requestBody = ET.fromstring(request.data) — тело запроса преобразуется в строку, затем в объект ElementTree.
response2.find(‘kpp’).text = requestBody.findtext(‘kpp’) — kpp достаём методом ET из преобразованного запроса и подставляем в ET ответа.
response2.find(‘inn’).text = request.headers.get(‘inn’) — inn достаём из заголовка запроса и подставляем.
return ET.tostring(response2) — здесь ET преобразуем обратно в строку, так как Flask с ET не работает.
{«customer-id»: request.args.get(‘id’), «Content-Type»: «application/xml»} — подставляем в заголовок customer-id значение из параметра запроса, прописываем Content-Type, чтобы клиент не интерпретировал ответ сервера как строку.
Начинаем проверку

Проверка на производительность
Для оценки производительности мы провели сравнительный тест между Flask и WireMock Standalone. Специально для этого были подготовлены заглушки — /api/postbalance и /api/token — на платформе WireMock.
Нагрузка генерировалась с помощью JMeter, используя две идентичные тред-группы, которые постепенно увеличивали интенсивность запросов в течение всего теста. Одна группа направлялась на Flask-заглушки, вторая — на WireMock.
Обе системы были развернуты в контейнерах Docker с ограничением памяти в 4 ГБ на каждый экземпляр. В таких условиях ожидать высокой производительности не стоит, однако тест позволил выявить особенности и ограничения каждого решения в реальных условиях нагрузочного тестирования.
Результаты: Wiremock


WireMock выдержал нагрузку до 69,5 запросов в секунду (RPS), однако при достижении примерно 60 RPS начали появляться ошибки.
Среднее время отклика составило 401 мс для одного из методов и 244 мс для второго.
Результаты: Flask


Flask превзошёл WireMock по производительности, выдержав максимальную нагрузку в 74,6 запросов в секунду (RPS) — предел, заданный в нашем тестовом сценарии — без единой ошибки.
Среднее время отклика для обоих методов составило примерно 130 миллисекунд, что значительно быстрее результатов WireMock.