Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Пирамида тестирования (Test Pyramid): Стратегия тестирования
Пирамида тестирования — это визуальная модель, которая рекомендует оптимальное соотношение и распределение различных типов тестов в проекте. Концепция предложена Mike Cohn и является best practice в индустрии.
Структура пирамиды
/\
/ \ End-to-End (E2E)
/----\ ~5% тестов
/ \
/--------\ Integration (API, БД)
/ \ ~15% тестов
/ \
/____________\ Unit
~80% тестов
Основной принцип: внизу пирамиды быстрые дешёвые тесты, вверху медленные дорогие.
1. Unit Tests (Нижняя часть — 80%)
Что тестировать:
- Отдельные функции и методы
- Бизнес-логика
- Вычисления и преобразования
- Валидацию
Пример для Flutter:
import 'package:flutter_test/flutter_test.dart';
void main() {
group('User Validation', () {
test('should validate email format', () {
// Arrange
final user = User(email: 'test@example.com');
// Act
final isValid = user.isEmailValid();
// Assert
expect(isValid, true);
});
test('should reject invalid email', () {
final user = User(email: 'invalid-email');
expect(user.isEmailValid(), false);
});
});
}
class User {
final String email;
User({required this.email});
bool isEmailValid() {
final emailRegex = RegExp(r'^[^@]+@[^@]+\.[^@]+$');
return emailRegex.hasMatch(email);
}
}
Преимущества:
- Очень быстрые (миллисекунды)
- Дешёвые (минимум зависимостей)
- Легко отлаживать
- Высокий coverage возможен
- Запускаются часто (на каждый коммит)
Чем писать:
flutter test # Запуск unit тестов
flutter test --coverage # С coverage отчётом
2. Integration Tests (Средняя часть — 15%)
Что тестировать:
- Взаимодействие между компонентами
- API запросы
- Работа с БД
- Потоки данных между слоями
Пример для Flutter:
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
void main() {
group('User API Integration', () {
late MockHttpClient mockHttpClient;
late UserRepository userRepository;
setUp(() {
mockHttpClient = MockHttpClient();
userRepository = UserRepository(httpClient: mockHttpClient);
});
test('should fetch user from API', () async {
// Arrange
when(mockHttpClient.get(any))
.thenAnswer((_) async => http.Response(
'{"id": 1, "name": "Alice"}',
200,
));
// Act
final user = await userRepository.getUser(1);
// Assert
expect(user.id, 1);
expect(user.name, 'Alice');
verify(mockHttpClient.get(any)).called(1);
});
});
}
Используемые инструменты:
# pubspec.yaml
dev_dependencies:
mockito: ^5.0.0
http_mock_adapter: ^4.0.0
Преимущества:
- Тестируют реальные взаимодействия
- Ловят баги на границах компонентов
- Помедленнее unit тестов
- Требуют больше setup
3. End-to-End Tests (Вершина — 5%)
Что тестировать:
- Полный пользовательский flow
- Реальные сценарии использования
- UI + логика + API
- Критические пути в приложении
Пример для Flutter:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('User Login E2E', () {
testWidgets('should login and see home page', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(MyApp());
// Act - Введи email
await tester.enterText(
find.byType(TextField).first,
'test@example.com',
);
// Act - Введи пароль
await tester.enterText(
find.byType(TextField).last,
'password123',
);
// Act - Нажми кнопку входа
await tester.tap(find.byText('Login'));
await tester.pumpAndSettle(); // Ждём анимации
// Assert - Должна быть главная страница
expect(find.byText('Welcome'), findsOneWidget);
});
});
}
Инструменты для E2E:
flutter drive --target=test_driver/app.dart
Или с Playwright MCP (для веба):
await browser.goto('https://app.example.com');
await browser.fill('input[type="email"]', 'test@example.com');
await browser.click('button:has-text("Login")');
Особенности:
- Медленные (секунды)
- Дорогие (полное приложение)
- Нестабильные (зависимость от окружения)
- Трудно отлаживать
- Их должно быть МАЛО
Почему именно эта пирамида?
Долгое время использовалась другая модель:
/\
/ \ Manual Testing (плохо!)
/----\
/ \ E2E Tests (слишком много)
/________\ Unit Tests
Проблемы старого подхода:
- Множество медленных E2E тестов
- Высокие затраты на поддержку
- Низкая скорость feedback
- Нестабильность
Пирамида решает эти проблемы:
- Быстрый feedback (unit тесты)
- Дешевизна (минимум зависимостей)
- Качество (несколько E2E для критичных путей)
Практический пример для Flutter приложения
Проект: Todo App
UNIT (80%): 80 тестов
- TodoItem validation
- TodoList filtering
- DateFormatter
- UserAuthService logic
- Database query builders
INTEGRATION (15%): 15 тестов
- TodoRepository с mock API
- LocalStorage integration
- AuthService с API
- DatabaseService
E2E (5%): 5 тестов
- Create and complete todo
- Login and view todos
- Search todos
- Delete completed todos
- Offline sync
Структура файлов
test/
├── unit/
│ ├── models/
│ │ └── todo_test.dart
│ ├── services/
│ │ └── todo_service_test.dart
│ └── utils/
│ └── date_formatter_test.dart
├── integration/
│ ├── repositories/
│ │ └── todo_repository_test.dart
│ └── services/
│ └── auth_service_test.dart
└── e2e/
├── app_test.dart
└── login_flow_test.dart
Команды для запуска
# Все unit тесты
flutter test test/unit/
# Все integration тесты
flutter test test/integration/
# E2E тесты
flutter drive --target=test_driver/app.dart
# Все тесты с coverage
flutter test --coverage
# Coverage report
genhtml coverage/lcov.info -o coverage/html
Лучшие практики
1. Пишите unit тесты ПЕРВЫМИ (TDD):
// Сначала тест
test('should calculate total price with tax', () {
expect(calculateTotal(100, 0.1), 110);
});
// Потом реализация
int calculateTotal(int price, double taxRate) {
return (price * (1 + taxRate)).toInt();
}
2. Избегайте дублирования тестов:
// Плохо
test('user login with valid email', () { ... });
test('user login with valid password', () { ... });
test('user login with valid email and password', () { ... });
// Хорошо
test('should login with valid credentials', () { ... });
3. Фокусируйтесь на критичные E2E:
// E2E только для:
// - Основные пользовательские сценарии
// - Payment flows
// - Authentication
// - Offline sync
// НЕ E2E:
// - Форматирование текста
// - UI деньги
// - Валидация полей (unit test!)
4. Поддерживайте целевой coverage:
Оптимально: 70-90% код coverage
Не гонитесь за 100% — есть код, который не нужно тестировать
Пирамида тестирования — это проверенная временем стратегия, обеспечивающая баланс между качеством, скоростью и затратами на тестирование.