Locust: нагрузочное тестирование

Подробнее на официальном сайте: https://docs.locust.io/en/latest/quickstart.html

Создание простого теста:

from locust import HttpUser, task, between

class Exists(HttpUser):
    wait_time = between(1, 5)

    @task(1)
    def auth(self):
        self.client.get("/exists")

Для проверки можно выполнить:

import requests as r
base_url = 'http://localhost:5007/release'
r.get(base_url + '/exists')

# <Response [200]>

Авторизация

from locust import HttpUser, task, between

class AuthorizationUser(HttpUser):
    wait_time = between(1, 5)

    @task(1)
    def auth(i):
        i.client.post('/auth', { "UserName": "user", "Password": "1234", "Version": "0.0.0.0" })

Для проверки в консоли можно выполнить:

import requests as r
base_url = 'http://localhost:5007/release'
r.post(base_url + '/auth', { "UserName": "user", "Password": "1234", "Version": "0.0.0.0" }) 
#<Response [200]>
>>> response = r.post(base_url + '/auth', { "UserName": "user", "Password": "1234", "Version": "0.0.0.0" })
response.json()
# {'token': 'dXNlcjoxMjM0', 'user': {'id': 2, 'login': 'user', 'claims': '.user.', 'date': '2022-06-21T04:14:27.559Z'}, 'ip': '::1'}

Переопределение статуса ответа:

def exists(i):
    with i.client.get('/exists', catch_response=True) as response:
        if response.status_code == 200:
            response.success()
        else:
            response.failure(f'status code is {response.status_code}')

Параметр catch_response=True обязателен, иначе вывод ошибки в response.failure не будет.

Группировка запросов для оптимального просмотра:

with self.client.get(f'/photos/{photo_id}', catch_response=True, name='/photos/[id]') as response:
    if response.status_code == 200:
            response.success()
        else:
            response.failure(f'status code is {response.status_code}')

Параметр name=’/photos/[id]’ предназначен для группировки результатов по «маске«.

Вывод ошибок будет таким

Создание собственного исключения:

class FlowException(Exception):
    pass
...
def check_flow(self):
    new_post = {...}
    response = self.client.post('/posts', json=new_post)
    if response.status_code != 201:
        raise FlowException('post not created')
    post_id = response.json().get('id')
  • raise FlowException(‘post not created’) — остановка сценария. Можно в сценарии указать и return, но при этом во вкладке Exception мы не увидим ошибки
Вкладка исключений
  • response.json().get(‘id’) — чтение id из результата

Сценарий временного хранения:

from locust import HttpLocust, TaskSet, task
import random as r

# class UserBehavior(TaskSet): 
#     def __init__(self, parent):
#         super(UserBehavior, self).__init__(parent)
#         self.created_posts = list()

class UserBehavior(TaskSet):
    created_posts = []

    @task(1)
    def create_post(self):
        ...
        post_id = response.json().get('id')
        self.created_posts.append(host_id)

    @task(10)
    def read_post(self):
        if len(self.created_posts) == 0:
            return
        post_id = r.chioce(self.created_posts)
  • r.chioce(self.created_posts) — рандомный выбор записи
  • в закомментированной части указан пример реализации, чтобы набор постов для каждого пользователя был своим

Выполнение под сценариев:

class Todo(TaskSet):
    @task(3)
    def index(self)
        self.client.get('/todos')
    
    @task(1)
    def stop(self):
        self.interrupt()

class UserBehavior(TaskSet):
     tasks = {Todo: 1}
   
     @task(3)
     def index(self):
         self.client.get('/')

     @task(2)
     def posts(self):
         self.client.get('/posts')
  • tasks = {Todo: 1} — объявление под задачи
  • self.interrupt() — чтобы принудительно завершить тест
Пример выполнения под теста

Передача заголовка авторизации:

...
import requests

class UserBehavior(TaskSet):
    def on_start(self):
        response = requests.post('/login', {...})
        self.client.headers.update({'Authorization': response.headers.get('RPC-Authorization')})
        # get token from cookies
        self.client.headers.update({'Authorization': response.cookies.get('RPC-Authorization')})
        # get token from body
        self.client.headers.update({'Authorization': str(response.content.decode().find('google'))})

     @task(21)
     def posts(self):
         ...

Распределенное тестирование.

  • Мастер — экземпляр, который собирает данные со slave «машин»
  • Slave — экземпляры, которые производят тестирование, и передают данные мастеру
locust -f test.py --master --host=http://localhost:3000
locust -f test.py --slave --master-host=http://localhost

Запуск теста без web-интерфейса:

locust -f test.py --host=http://localhost --no-web -c 10 -r 2 --run-time 1m --csv=test_res
  • no-web — не создаем веб-интерфейс
  • c — количество пользователей
  • r — прирастание пользователей в секунду
  • run-time — время теста в минутах (параметр времени)
  • csv — результат теста

Игнорирование сертификата HTTPS:

self.client.post('/posts', data, verify=False)

verify=False — не будет проверки сертификата

Логирование:

locust -f main.py --host=http://localhost --logfile=locustfile.log

Подробнее тут https://docs.locust.io/en/stable/logging.html

Статья с Хабр

Дополнительное видео:

Нагрузочное тестирование с Locust
Нагрузочное тестирование с Locust. Часть 2
Нагрузочное тестирование с Locust. Часть 3
Print Friendly, PDF & Email

Добавить комментарий