Подробнее на официальном сайте: 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
Статья с Хабр
Дополнительное видео: