Статья 7: Тестирование производительности и нагрузки: Полное руководство для QA инженеров
Тестирование производительности и нагрузки (Performance & Load Testing)
Как тестировать производительность, находить узкие места (Bottlenecks) и обеспечивать стабильность под нагрузкой

📍 Вы здесь:
[✓] Статья 1: Основы QA (QA Fundamentals)
[✓] Статья 2: Практика QA (QA Practice)
[✓] Статья 3: Структуры данных и алгоритмы (DSA for QA)
[✓] Статья 4: Фреймворки автоматизации (Automation Frameworks)
[✓] Статья 5: Непрерывная интеграция (CI/CD)
[✓] Статья 6: Техники тест-дизайна (Test Design Techniques)
[→] Статья 7: Тестирование производительности (Performance Testing) ← Сейчас читаете
[ ] Статья 8: Как получить работу в Apple (Landing Your Dream Job)
Прогресс: 87% ✨
Ваше приложение работает отлично на вашем ноутбуке. Но что произойдет, когда 10,000 пользователей зайдут одновременно? Когда база данных вырастет до 10 миллионов записей? Когда API будет получать 1000 запросов в секунду?
Добро пожаловать в тестирование производительности (Performance Testing) — критический навык, который отличает Senior QA от Middle.
Из реальной вакансии Apple Software Quality Engineer:
“Опыт тестирования и анализа производительности (Experience with performance testing and analysis)”
В этой статье вы научитесь:
- Понимать метрики производительности
- Проводить нагрузочное (Load) и стресс-тестирование (Stress Testing)
- Использовать JMeter, K6, Gatling
- Находить и анализировать узкие места (Bottlenecks)
- Интегрировать тесты производительности в CI/CD
📋 Содержание
- Типы тестирования производительности (Performance Testing Types)
- Ключевые метрики производительности (Key Performance Metrics)
- JMeter: Отраслевой стандарт (Industry Standard)
- K6: Современный JavaScript подход (Modern JavaScript Approach)
- Gatling: Мощь Scala (The Power of Scala)
- Реальные сценарии (Real-World Scenarios)
- Анализ результатов и поиск узких мест (Bottleneck Analysis)
- Тестирование производительности в CI/CD (Performance Testing in CI/CD)
- Лучшие практики (Best Practices)
- Вопросы на собеседовании (Interview Questions)
🎯 Типы тестирования производительности
1. Нагрузочное тестирование (Load Testing)
Цель: Проверить, как система ведёт себя под ожидаемой нагрузкой
Пример:
- Обычно: 1,000 одновременных пользователей
- Black Friday: 50,000 одновременных пользователей
Вопросы:
- Выдержит ли система планируемую нагрузку?
- Какое response time при нормальной нагрузке?
// K6 Load Test Example
import http from 'k6/http';
import { sleep, check } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 100 }, // Рост до 100 пользователей
{ duration: '5m', target: 100 }, // Держим 100 пользователей
{ duration: '2m', target: 0 }, // Снижение до 0
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95% запросов под 500ms
http_req_failed: ['rate<0.01'], // <1% ошибок
},
};
export default function() {
const res = http.get('https://api.example.com/products');
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
});
sleep(1);
}
2. Стресс-тестирование (Stress Testing)
Цель: Найти точку отказа системы
Сценарий: Постепенно увеличиваем нагрузку до тех пор, пока система не упадет
Вопросы:
- Когда система начинает деградировать?
- Как она восстанавливается после падения?
- Какие компоненты падают первыми?
// K6 Stress Test
export const options = {
stages: [
{ duration: '2m', target: 100 }, // Нормальная нагрузка
{ duration: '5m', target: 200 }, // Около точки отказа
{ duration: '2m', target: 300 }, // За пределами возможностей
{ duration: '5m', target: 400 }, // Сильно за пределами
{ duration: '10m', target: 0 }, // Восстановление
],
};
export default function() {
const res = http.get('https://api.example.com/products');
check(res, {
'status is not 500': (r) => r.status !== 500,
});
sleep(1);
}
3. Тестирование пиковой нагрузки (Spike Testing)
Цель: Проверить реакцию на внезапный всплеск нагрузки
Пример:
- Рекламная кампания запустилась
- Viral post в социальных сетях
- Flash sale начался
// K6 Spike Test
export const options = {
stages: [
{ duration: '10s', target: 100 }, // Нормально
{ duration: '1m', target: 2000 }, // SPIKE!
{ duration: '3m', target: 2000 }, // Держим spike
{ duration: '10s', target: 100 }, // Возврат к нормальному
{ duration: '3m', target: 100 }, // Восстановление
],
};
4. Тестирование длительной нагрузки (Soak/Endurance Testing)
Цель: Проверить стабильность при длительной нагрузке
Проблемы, которые находит:
- Утечки памяти (Memory Leaks)
- Утечки соединений с БД (Connection Leaks)
- Проблемы с дисковым пространством
- Рост лог-файлов
// K6 Soak Test - работает часами
export const options = {
stages: [
{ duration: '2m', target: 400 }, // Рост
{ duration: '3h56m', target: 400 }, // Держим ~4 часа
{ duration: '2m', target: 0 }, // Снижение
],
};
📊 Ключевые метрики производительности
1. Время отклика (Response Time)
Определение: Время от отправки запроса до получения ответа
Метрики:
- Среднее время отклика (Average Response Time) - среднее время
- Медиана (50-й процентиль) - половина запросов быстрее
- 90-й процентиль (p90) - 90% запросов быстрее
- 95-й процентиль (p95) - 95% запросов быстрее
- 99-й процентиль (p99) - 99% запросов быстрее
Почему процентили важнее среднего:
Пример:
9 запросов: 100ms каждый
1 запрос: 10,000ms (timeout)
Average: 1,090ms (выглядит плохо)
95th percentile: 100ms (реально хорошо для 95% пользователей)
Целевые значения:
| Тип операции | Цель |
|---|---|
| Загрузка страницы | < 2 секунд |
| API GET | < 200ms |
| API POST | < 500ms |
| Поиск | < 1 секунда |
| Запрос к БД | < 100ms |
2. Пропускная способность (Throughput)
Определение: Количество запросов в единицу времени
Метрики:
- Запросов в секунду (Requests per second, RPS)
- Транзакций в секунду (Transactions per second, TPS)
- Страниц в минуту (Pages per minute)
Пример:
Хорошо: 1000 RPS с 200ms response time
Плохо: 1000 RPS с 5000ms response time
3. Частота ошибок (Error Rate)
Определение: Процент неуспешных запросов
Целевые значения:
- Production: < 0.1% (99.9% успех)
- Staging: < 1%
- Критические API: < 0.01% (99.99% успех)
Типы ошибок:
- 4xx ошибки (ошибки клиента)
- 5xx ошибки (ошибки сервера)
- Timeouts
- Ошибки соединения
4. Одновременные пользователи (Concurrent Users)
Определение: Количество пользователей, активных одновременно
Важно понимать разницу:
Всего пользователей (Total Users): 10,000
Активных пользователей (Active Users): 3,000 (онлайн сейчас)
Одновременных пользователей (Concurrent Users): 500 (делают запросы СЕЙЧАС)
Формула для расчёта:
Одновременные пользователи = (Всего пользователей × Процент использования) / Время ожидания
5. Индекс удовлетворённости (Apdex Score)
Определение: Индекс производительности приложения (Application Performance Index) — от 0 до 1
Формула:
Apdex = (Удовлетворённые + (Терпеливые / 2)) / Всего запросов
Где:
- Удовлетворённые (Satisfied): Время отклика ≤ T
- Терпеливые (Tolerating): T < Время отклика ≤ 4T
- Разочарованные (Frustrated): Время отклика > 4T
Пример:
T = 500ms (целевое значение)
100 запросов:
- 70 под 500ms (Удовлетворённые)
- 20 между 500ms-2000ms (Терпеливые)
- 10 более 2000ms (Разочарованные)
Apdex = (70 + 20/2) / 100 = 0.8 (Хорошо)
Рейтинг:
- 1.0 - 0.94 = Отлично
- 0.93 - 0.85 = Хорошо
- 0.84 - 0.70 = Удовлетворительно
- 0.69 - 0.50 = Плохо
- < 0.50 = Неприемлемо
🔨 JMeter: Отраслевой стандарт (Industry Standard)
Почему JMeter?
Преимущества:
- ✅ Отраслевой стандарт (20+ лет)
- ✅ Огромное сообщество
- ✅ Графический интерфейс (GUI) для создания тестов
- ✅ Поддержка множества протоколов
- ✅ Бесплатный и с открытым исходным кодом (Open-source)
Недостатки:
- ❌ На Java (тяжёлый)
- ❌ Графический интерфейс не подходит для CI/CD
- ❌ Сложная кривая обучения
Базовая установка
# Скачать JMeter
wget https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.6.3.tgz
tar -xzf apache-jmeter-5.6.3.tgz
cd apache-jmeter-5.6.3/bin
# Запустить GUI
./jmeter
# Запустить в CLI (для CI/CD)
./jmeter -n -t test-plan.jmx -l results.jtl -e -o report/
Создание простого теста
Структура Test Plan:
Test Plan
├── Thread Group (Users)
│ ├── HTTP Request (API call)
│ ├── HTTP Header Manager
│ ├── JSON Assertions
│ └── Response Time Assertions
├── Listeners (Results)
│ ├── View Results Tree
│ ├── Summary Report
│ └── Aggregate Report
Пример: API Load Test
Настройки Thread Group:
Number of Threads: 100
Ramp-up Period: 60 секунд
Loop Count: 10
HTTP Request:
Server: api.example.com
Port: 443
Protocol: https
Method: GET
Path: /api/v1/products
Assertions:
// Response Code
Response Code: 200
// Response Time
Response Time: <= 500ms
// JSON Body
$.data.length > 0
Продвинутый уровень: Параметризация
CSV Data Set Config:
users.csv:
email,password
user1@test.com,pass123
user2@test.com,pass456
user3@test.com,pass789
HTTP Request с переменными:
POST /api/login
Body:
{
"email": "${email}",
"password": "${password}"
}
⚡ K6: Современный JavaScript подход
Почему K6?
Преимущества:
- ✅ JavaScript (знакомый синтаксис для QA)
- ✅ Командная строка в приоритете (CLI-first) — отлично для CI/CD
- ✅ Лёгкий и быстрый
- ✅ Встроенные результаты в JSON
- ✅ Облачная интеграция (Cloud Integration)
Недостатки:
- ❌ Нет графического интерфейса (GUI)
- ❌ Меньше протоколов чем JMeter
- ❌ Относительно молодой (2017)
Установка
# macOS
brew install k6
# Linux
sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
# Windows
choco install k6
# Docker
docker pull grafana/k6
Базовый тест
simple-test.js:
import http from 'k6/http';
import { sleep, check } from 'k6';
export const options = {
vus: 10, // Виртуальные пользователи
duration: '30s', // Длительность теста
};
export default function() {
const res = http.get('https://api.example.com/products');
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 200ms': (r) => r.timings.duration < 200,
});
sleep(1);
}
Запуск:
k6 run simple-test.js
Продвинутый уровень: Сценарии
multi-scenario.js:
import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
scenarios: {
// Сценарий 1: Просмотр продуктов
browse: {
executor: 'constant-vus',
vus: 50,
duration: '5m',
exec: 'browseProducts',
},
// Сценарий 2: Поиск
search: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '2m', target: 20 },
{ duration: '5m', target: 20 },
{ duration: '2m', target: 0 },
],
exec: 'searchProducts',
},
// Сценарий 3: Оформление заказа (тяжелая операция)
checkout: {
executor: 'constant-arrival-rate',
rate: 10,
timeUnit: '1s',
duration: '5m',
preAllocatedVUs: 50,
exec: 'checkoutFlow',
},
},
thresholds: {
'http_req_duration{scenario:browse}': ['p(95)<500'],
'http_req_duration{scenario:search}': ['p(95)<1000'],
'http_req_duration{scenario:checkout}': ['p(95)<2000'],
'http_req_failed': ['rate<0.01'],
},
};
export function browseProducts() {
http.get('https://api.example.com/products');
sleep(1);
}
export function searchProducts() {
http.get('https://api.example.com/products/search?q=laptop');
sleep(2);
}
export function checkoutFlow() {
// Логин
const loginRes = http.post('https://api.example.com/auth/login', {
email: 'test@example.com',
password: 'pass123',
});
const token = loginRes.json('token');
// Добавить в корзину
http.post('https://api.example.com/cart',
JSON.stringify({ productId: 123, quantity: 1 }),
{ headers: { 'Authorization': `Bearer ${token}` } }
);
// Оформление заказа
http.post('https://api.example.com/checkout',
JSON.stringify({ paymentMethod: 'card' }),
{ headers: { 'Authorization': `Bearer ${token}` } }
);
sleep(3);
}
Кастомные метрики
import http from 'k6/http';
import { Trend, Counter } from 'k6/metrics';
// Кастомные метрики
const loginDuration = new Trend('login_duration');
const checkoutErrors = new Counter('checkout_errors');
export default function() {
const start = Date.now();
const res = http.post('https://api.example.com/login', {...});
const duration = Date.now() - start;
loginDuration.add(duration);
if (res.status !== 200) {
checkoutErrors.add(1);
}
}
K6 в CI/CD
GitHub Actions:
name: Performance Tests
on: [push]
jobs:
k6-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run K6 test
uses: grafana/k6-action@v0.3.1
with:
filename: performance-tests/load-test.js
flags: --out json=results.json
- name: Upload results
uses: actions/upload-artifact@v3
with:
name: k6-results
path: results.json
🎯 Gatling: Мощь Scala (The Power of Scala)
Почему Gatling?
Преимущества (Advantages):
- ✅ Очень высокая производительность (Very High Performance)
- ✅ Отличные отчеты (Excellent Reports) - лучшие в индустрии
- ✅ Отлично подходит для HTTP/WebSocket
- ✅ Переиспользуемый код симуляций (Reusable Simulation Code)
Недостатки (Disadvantages):
- ❌ Scala - сложнее для QA без навыков программирования (Difficult for non-programmers)
- ❌ Меньше протоколов (Fewer Protocols)
- ❌ Сообщество меньше чем у JMeter (Smaller Community)
Установка
# Скачать
wget https://repo1.maven.org/maven2/io/gatling/highcharts/gatling-charts-highcharts-bundle/3.10.3/gatling-charts-highcharts-bundle-3.10.3-bundle.zip
unzip gatling-charts-highcharts-bundle-3.10.3-bundle.zip
cd gatling-charts-highcharts-bundle-3.10.3
# Запустить recorder (для записи сценария)
./bin/recorder.sh
# Запустить симуляцию
./bin/gatling.sh
Базовая симуляция
BasicSimulation.scala:
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
class BasicSimulation extends Simulation {
val httpProtocol = http
.baseUrl("https://api.example.com")
.acceptHeader("application/json")
.userAgentHeader("Gatling Performance Test")
val scn = scenario("Basic Load Test")
.exec(
http("Get Products")
.get("/api/products")
.check(status.is(200))
.check(jsonPath("$.data").exists)
.check(responseTimeInMillis.lte(500))
)
.pause(1)
setUp(
scn.inject(
rampUsers(100).during(60.seconds) // 100 пользователей за 60 секунд
).protocols(httpProtocol)
).assertions(
global.responseTime.max.lt(5000),
global.successfulRequests.percent.gt(95)
)
}
🔍 Реальные сценарии (Real-World Scenarios)
Сценарий 1: E-commerce Black Friday (Чёрная Пятница)
Требования (Requirements):
- Обычный режим: 5,000 одновременных пользователей (Concurrent Users)
- Чёрная Пятница: 100,000 одновременных пользователей
- Ожидается пиковая нагрузка (Spike) в 0:00
Стратегия тестирования (Testing Strategy):
// K6 Black Friday Simulation
export const options = {
stages: [
// До полуночи: нормальный трафик
{ duration: '30m', target: 5000 },
// Полночь - spike!
{ duration: '2m', target: 100000 },
// Держим пик
{ duration: '1h', target: 100000 },
// Постепенное снижение
{ duration: '30m', target: 50000 },
{ duration: '1h', target: 20000 },
{ duration: '2h', target: 5000 },
],
thresholds: {
'http_req_duration{critical:yes}': ['p(99)<1000'], // Критические страницы
'http_req_duration{critical:no}': ['p(99)<5000'], // Некритические
'http_req_failed': ['rate<0.1'], // 0.1% ошибок OK
},
};
Сценарий 2: API Rate Limiting
Требования:
- Лимит API: 1000 запросов/минуту на пользователя
- Нужно проверить работу rate limiting
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
scenarios: {
// Нормальное использование - должно работать
normal: {
executor: 'constant-arrival-rate',
rate: 900, // 900 req/min (под лимитом)
timeUnit: '1m',
duration: '5m',
preAllocatedVUs: 50,
exec: 'normalRequest',
},
// Чрезмерное использование - должен быть rate limit
excessive: {
executor: 'constant-arrival-rate',
rate: 1500, // 1500 req/min (над лимитом!)
timeUnit: '1m',
duration: '5m',
preAllocatedVUs: 100,
exec: 'excessiveRequest',
},
},
};
export function normalRequest() {
const res = http.get('https://api.example.com/data');
check(res, {
'normal request succeeds': (r) => r.status === 200,
});
}
export function excessiveRequest() {
const res = http.get('https://api.example.com/data');
check(res, {
'excessive request rate limited': (r) => r.status === 429,
'has retry-after header': (r) => r.headers['Retry-After'] !== undefined,
});
}
📈 Анализ результатов и поиск узких мест (Bottleneck Analysis)
Чтение отчётов тестирования производительности (Performance Testing Reports)
Ключевые разделы для анализа (Key Sections):
- Сводная статистика (Summary Statistics)
Requests: 10,000
Duration: 5 минут
Success Rate: 98.5%
Avg Response Time: 450ms
p95 Response Time: 850ms
p99 Response Time: 2,150ms
Max Response Time: 5,200ms
Интерпретация:
- ✅ 98.5% success rate (хорошо, > 99% идеально)
- ✅ Average 450ms (приемлемо для большинства API)
- ⚠️ p99 2.15s (1.5% пользователей ждут > 2s)
- ❌ Max 5.2s (требует расследования!)
- Распределение Response Time
0-100ms: 15% (1,500 запросов)
100-300ms: 45% (4,500 запросов)
300-500ms: 25% (2,500 запросов)
500-1000ms: 10% (1,000 запросов)
1000-2000ms: 3% (300 запросов)
2000ms+: 2% (200 запросов) ← Исследовать!
- Распределение ошибок
200 OK: 9,850 (98.5%)
400 Bad Request: 50 (0.5%)
429 Rate Limit: 75 (0.75%)
500 Server Error: 20 (0.2%) ← Критично!
504 Timeout: 5 (0.05%) ← Критично!
Типичные паттерны Bottlenecks
Паттерн 1: Постепенная деградация производительности
Симптом:
Минута 1: p95 = 300ms
Минута 2: p95 = 350ms
Минута 3: p95 = 450ms
Минута 4: p95 = 650ms
Минута 5: p95 = 950ms
Вероятные причины:
- Утечка памяти
- Пул соединений не освобождается
- Кэш не очищается
- Деградация планов запросов БД
Как диагностировать:
# Мониторинг использования памяти
kubectl top pods --namespace=production
# Проверка соединений с БД
SELECT count(*) FROM pg_stat_activity;
# Мониторинг garbage collection
jstat -gc <pid> 1000
Паттерн 2: Внезапный рост ошибок
Симптом:
Минуты 1-3: 0% ошибок
Минута 4: 15% ошибок (500 Internal Server Error)
Минута 5: 35% ошибок
Вероятные причины:
- Пул потоков исчерпан
- Пул соединений с БД полон
- Выход за пределы памяти
- Сработал circuit breaker
Паттерн 3: Бимодальное время ответа
Симптом:
80% запросов: 100-200ms
20% запросов: 5000-6000ms
Нет постепенного распределения!
Вероятные причины:
- Cache hit vs cache miss
- Отставание read replica
- Cold start (serverless)
- Проблемы с DNS
🔄 Тестирование производительности в CI/CD (Performance Testing in CI/CD)
Стратегия интеграции (Integration Strategy)
Когда запускать тесты производительности (When to Run Performance Tests):
- На Pull Request — Дымовые тесты производительности (Smoke Performance Tests)
- Ночью (Nightly) — Полные нагрузочные тесты (Full Load Tests)
- Перед деплоем в Production — Стресс-тесты (Stress Tests)
- По расписанию (Scheduled) — Soak-тесты (еженедельно)
Пример GitHub Actions (GitHub Actions Example)
name: Performance Tests
on:
pull_request:
branches: [ main ]
schedule:
- cron: '0 2 * * *' # Каждую ночь в 2 AM
workflow_dispatch:
jobs:
smoke-test:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v3
- name: Install K6
run: |
sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
- name: Run smoke test
run: k6 run --out json=results.json tests/smoke-test.js
- name: Check thresholds
run: |
if grep -q '"thresholds".*"failed":true' results.json; then
echo "❌ Performance thresholds failed!"
exit 1
fi
✅ Лучшие практики (Best Practices)
1. Начинайте с малого, масштабируйте постепенно (Start Small, Scale Up)
Неправильно (Wrong Approach):
// Не делайте так!
export const options = {
vus: 10000, // Слишком много слишком быстро
duration: '10s',
};
Правильно (Correct Approach):
// Делайте так!
export const options = {
stages: [
{ duration: '2m', target: 10 }, // Начинаем с малого
{ duration: '5m', target: 50 }, // Постепенный рост
{ duration: '10m', target: 100 }, // Целевая нагрузка
{ duration: '5m', target: 200 }, // Увеличиваем
{ duration: '5m', target: 0 }, // Снижение (Ramp-down)
],
};
2. Используйте реалистичное время ожидания (Use Realistic Think Time)
Неправильно:
export default function() {
http.get('/api/products');
http.post('/api/cart', {...});
http.post('/api/checkout', {...});
// Нет пауз - нереалистично!
}
Правильно:
export default function() {
http.get('/api/products');
sleep(randomBetween(2, 5)); // Пользователь изучает продукты
http.post('/api/cart', {...});
sleep(randomBetween(1, 3)); // Пользователь проверяет корзину
http.post('/api/checkout', {...});
sleep(randomBetween(5, 10)); // Пользователь заполняет форму
}
function randomBetween(min, max) {
return Math.random() * (max - min) + min;
}
3. Мониторьте системные ресурсы
Во время тестов мониторьте:
# Использование CPU
top
# Память
free -h
# Disk I/O
iostat -x 1
# Сеть
iftop
# Логи приложения
tail -f /var/log/application.log
# Соединения с БД
watch 'psql -c "SELECT count(*) FROM pg_stat_activity;"'
4. Тестируйте в Production-подобной среде
Чек-лист:
- Те же характеристики сервера (CPU, RAM, диск)
- Та же сетевая задержка
- Тот же размер базы данных
- Те же внешние зависимости
- Та же конфигурация балансировщика
- Тот же CDN/кэш слой
5. Изолируйте тесты
Не делайте:
// Не смешивайте разные типы тестов
export default function() {
http.get('/api/products'); // Легкая операция
http.post('/api/heavy-report'); // Тяжелая операция
// Результаты будут запутанными!
}
Делайте:
// Разделяйте сценарии
export const options = {
scenarios: {
light_operations: {
exec: 'lightOps',
executor: 'constant-vus',
vus: 100,
},
heavy_operations: {
exec: 'heavyOps',
executor: 'constant-vus',
vus: 10, // Меньше VUs для тяжелых операций
},
},
};
export function lightOps() {
http.get('/api/products');
}
export function heavyOps() {
http.post('/api/heavy-report');
}
❓ Вопросы на собеседовании (Interview Questions)
Вопрос 1: “В чём разница между нагрузочным тестированием (Load Testing) и стресс-тестированием (Stress Testing)?”
Хороший ответ (Good Answer):
“Нагрузочное тестирование (Load Testing) проверяет работу системы при ожидаемой нагрузке — например, тестируем, выдержит ли наш e-commerce сайт 10,000 одновременных пользователей при обычной работе. Стресс-тестирование (Stress Testing) выходит за пределы возможностей системы, чтобы найти точку отказа — мы продолжаем увеличивать нагрузку пока система не упадёт, затем наблюдаем как она деградирует и восстанавливается. Нагрузочное тестирование — это валидация; стресс-тестирование — это поиск лимитов.”
Вопрос 2: “Какие метрики вы мониторите при тестировании производительности (Performance Testing)?”
Хороший ответ (Good Answer):
“Я фокусируюсь на пяти ключевых метриках: Время отклика (Response Time) — особенно перцентили p95 и p99, не только среднее; Пропускная способность (Throughput) — запросов в секунду; Частота ошибок (Error Rate) — должна быть под 1%; Использование ресурсов (Resource Utilization) — CPU, память, disk I/O; и Одновременные пользователи (Concurrent Users). Также отслеживаю кастомные бизнес-метрики вроде времени оформления заказа или задержки поиска.”
Вопрос 3: “Как бы вы интегрировали тесты производительности (Performance Tests) в CI/CD?”
Хороший ответ (Good Answer):
“Я использую многоуровневый подход: быстрые дымовые тесты (Smoke Tests) на каждый PR для поимки очевидных регрессий, ночные нагрузочные тесты (Load Tests) с реалистичными сценариями, еженедельные soak-тесты для поиска утечек памяти. Я настраиваю автоматические пороги (Thresholds) — если p95 время отклика превышает 500ms или частота ошибок выше 1%, пайплайн падает. Результаты постятся в Slack и хранятся для исторического сравнения.”
Вопрос 4: “Вы заметили постепенное увеличение времени отклика (Response Times) во время нагрузочного теста. Что бы вы исследовали?”
Хороший ответ (Good Answer):
“Постепенная деградация обычно указывает на утечки ресурсов (Resource Leaks). Я бы проверил: использование памяти на утечки (Memory Leaks), пул соединений с БД на освобождение соединений (Connection Pool), время выполнения запросов на деградацию планов, и логи приложения на предупреждения. Также проверил бы работу кэширования (Caching) — возможно кэш заполняется или eviction сломан.”
🎯 Ключевые выводы (Key Takeaways)
Сводка типов тестирования производительности (Performance Testing Types Summary)
| Тип (Type) | Цель (Purpose) | Длительность (Duration) | Паттерн нагрузки (Load Pattern) |
|---|---|---|---|
| Нагрузочное (Load) | Валидация ожидаемой нагрузки | 10-60 мин | Стабильная (Steady) |
| Стрессовое (Stress) | Найти точку отказа | 30-60 мин | Растущая (Increasing) |
| Пиковое (Spike) | Обработка внезапных всплесков | 5-15 мин | Внезапные пики (Sudden Peaks) |
| Длительное (Soak) | Найти утечки памяти (Memory Leaks) | 4-24 часа | Постоянная (Steady) |
Сравнение инструментов (Tool Comparison)
| Инструмент (Tool) | Лучше для (Best For) | Кривая обучения (Learning Curve) | Готов к CI/CD (CI/CD Ready) |
|---|---|---|---|
| JMeter | Сложных протоколов | Средняя (Medium) | ⚠️ Нужен CLI |
| K6 | Современных web-приложений | Лёгкая (Easy) | ✅ Нативно |
| Gatling | Высокой производительности | Сложная (Hard) | ✅ Нативно |
📍 Что дальше?
Завтра - ФИНАЛ!
🍎 Статья 8: Как получить работу в Apple
- Анализ требований Apple к кандидатам
- Оптимизация резюме для FAANG
- Руководство по GitHub портфолио
- Процесс собеседований (4 раунда)
- Переговоры о зарплате ($140K-$200K+)
- Первые 90 дней в Apple
Это кульминация всего, что мы изучили!
💡 Финальный совет
Performance testing - это не одноразовая активность:
Неделя 1: Базовые тесты
Недели 2-4: Разработка
Неделя 4: Проверка performance регрессий
Неделя 5: Load test
Неделя 6: Stress test
Неделя 8: Soak test
Production: Непрерывный мониторинг
Сделайте это частью цикла разработки, а не afterthought!
Была ли статья полезна? 👏
Вопросы? Пишите в комментариях!
Автор: AAnnayev — Senior SDET
Теги: #PerformanceTesting #LoadTesting #JMeter #K6 #Gatling #QA #SDET