MyModus использует комплексную систему тестирования для обеспечения качества кода, стабильности приложения и корректной работы всех компонентов. Документация описывает структуру тестов, их запуск и интерпретацию результатов.
┌─────────────────────────────────────────────────────────────┐
│ Testing Architecture │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
│ │ Unit Tests │ │ Widget Tests │ │Integration │ │
│ │ │ │ │ │ Tests │ │
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
│ │ Platform Tests │ │ Coverage Tests │ │Performance │ │
│ │ │ │ │ │ Tests │ │
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Test Runners │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
│ │ Bash Script │ │PowerShell Script│ │Flutter CLI │ │
│ │ (Linux/Mac) │ │ (Windows) │ │ Tests │ │
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
test/
├── widget_tests.dart # Widget тесты UI компонентов
├── integration_tests.dart # Интеграционные тесты API/Web3
├── platform_tests.dart # Тесты для разных платформ
├── test_config.dart # Конфигурация тестов
├── unit/ # Unit тесты (будущие)
│ ├── providers/ # Тесты провайдеров
│ ├── services/ # Тесты сервисов
│ └── models/ # Тесты моделей
└── mocks/ # Автогенерируемые моки
├── widget_tests.mocks.dart
└── integration_tests.mocks.dart
test/
├── ai_services_test.dart # Тесты AI сервисов
├── auth_service_test.dart # Тесты аутентификации
├── product_service_test.dart # Тесты продуктов
├── web3_service_test.dart # Тесты Web3
└── integration/ # Интеграционные тесты
├── api_test.dart # Тесты API endpoints
└── database_test.dart # Тесты базы данных
cd frontend
chmod +x scripts/run_tests.sh
./scripts/run_tests.sh [команда]cd frontend
.\scripts\run_tests.ps1 [команда]# Все тесты
flutter test
# Конкретный файл тестов
flutter test test/widget_tests.dart
# Тесты с покрытием
flutter test --coverage
# Тесты в watch режиме
flutter test --watch
# Тесты для конкретной платформы
flutter test --platform=android
flutter test --platform=ios
flutter test --platform=web| Команда | Описание |
|---|---|
all |
Запустить все тесты (по умолчанию) |
unit |
Только unit тесты |
widget |
Только widget тесты |
integration |
Только интеграционные тесты |
platform |
Только платформенные тесты |
coverage |
Тесты с покрытием |
watch |
Тесты в watch режиме |
android |
Тесты для Android |
ios |
Тесты для iOS |
web |
Тесты для Web |
analyze |
Анализ качества кода |
help |
Показать справку |
Тестируют отдельные функции, методы и классы в изоляции.
test('should calculate total price correctly', () {
final cart = ShoppingCart();
cart.addItem(Product(price: 100));
cart.addItem(Product(price: 200));
expect(cart.totalPrice, equals(300));
});Покрытие: Логика бизнес-функций, вычисления, валидация.
Тестируют UI компоненты и их поведение.
testWidgets('should display product information correctly', (WidgetTester tester) async {
final product = Product(title: 'Test Product', price: 1000);
await tester.pumpWidget(ProductCard(product: product));
expect(find.text('Test Product'), findsOneWidget);
expect(find.text('1000 ₽'), findsOneWidget);
});Покрытие: UI компоненты, навигация, пользовательские взаимодействия.
Тестируют взаимодействие между компонентами системы.
test('should fetch products from API successfully', () async {
final apiService = ApiService();
final products = await apiService.getProducts();
expect(products, isA<List<Product>>());
expect(products.isNotEmpty, true);
});Покрытие: API интеграция, Web3 операции, работа с базой данных.
Тестируют поведение на разных платформах и устройствах.
testWidgets('should adapt to different screen sizes', (WidgetTester tester) async {
// Тестируем мобильное устройство
tester.binding.window.physicalSizeTestValue = Size(375, 812);
await tester.pumpWidget(HomePage());
// Проверяем мобильную компоновку
expect(find.byType(CustomBottomNavigationBar), findsOneWidget);
});Покрытие: Адаптивность, платформо-специфичные функции, жесты.
class MyModusTestConfig {
// Настройки таймаутов
static const WidgetTestSettings widgetTestSettings = WidgetTestSettings(
timeout: Timeout(Duration(minutes: 5)),
skip: false,
);
// Настройки для CI/CD
static void configureCITests() {
test.setTimeout(Duration(minutes: 30));
test.setMaxConcurrency(1);
test.setLogWriterOnFailure(true);
}
}# .env.test
FLUTTER_TEST=true
API_BASE_URL=http://localhost:8080
WEB3_NETWORK_ID=1337
OPENAI_API_KEY=test_key# Запуск тестов с покрытием
flutter test --coverage
# Генерация HTML отчета (требует lcov)
genhtml coverage/lcov.info -o coverage/html- Цель: Минимум 80% покрытия кода
- Критические компоненты: 90%+ покрытия
- UI компоненты: 70%+ покрытия
coverage/
├── lcov.info # LCOV формат
├── html/ # HTML отчет
│ ├── index.html # Главная страница
│ ├── dart/ # Детали по файлам
│ └── css/ # Стили отчета
└── summary.txt # Краткий отчет
class TestUtils {
// Создание тестового пользователя
static Map<String, dynamic> createTestUser() {
return {
'id': 'test_user_1',
'email': 'test@mymodus.com',
'username': 'testuser',
// ... другие поля
};
}
// Создание тестового продукта
static Map<String, dynamic> createTestProduct() {
return {
'id': 'test_product_1',
'title': 'Test Product',
'price': 1000,
// ... другие поля
};
}
}// Создание моков для API
final mockResponse = http.Response(
'{"products": [{"id": "1", "title": "Test Product"}]}',
200,
);
when(mockHttpClient.get(any, headers: anyNamed('headers')))
.thenAnswer((_) async => mockResponse);# Анализ Dart кода
flutter analyze
# Анализ с исправлением ошибок
flutter analyze --fix# Проверка форматирования
flutter format --dry-run .
# Автоматическое форматирование
flutter format .# analysis_options.yaml
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
errors:
invalid_annotation_target: ignore
missing_required_param: error
missing_return: errortest('should handle API errors gracefully', () {
when(mockHttpClient.get(any))
.thenThrow(Exception('Network error'));
expect(
() => apiService.getProducts(),
throwsA(isA<Exception>()),
);
});testWidgets('should display error messages', (WidgetTester tester) async {
// Устанавливаем ошибку
aiProvider.setError('Произошла ошибка при загрузке');
await tester.pumpWidget(AIRecommendationsScreen());
// Проверяем отображение ошибки
expect(find.text('Произошла ошибка при загрузке'), findsOneWidget);
expect(find.text('Повторить'), findsOneWidget);
});group('Mobile Platform Tests', () {
testWidgets('should display mobile-optimized layout', (WidgetTester tester) async {
// Симулируем мобильное устройство
tester.binding.window.physicalSizeTestValue = Size(375, 812);
tester.binding.window.devicePixelRatioTestValue = 3.0;
// Тестируем мобильную компоновку
await tester.pumpWidget(HomePage());
expect(find.byType(CustomBottomNavigationBar), findsOneWidget);
});
});group('Web Platform Tests', () {
testWidgets('should handle web keyboard navigation', (WidgetTester tester) async {
tester.binding.window.physicalSizeTestValue = Size(1920, 1080);
await tester.pumpWidget(WebForm());
// Тестируем навигацию по Tab
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.pump();
});
});testWidgets('should handle memory pressure', (WidgetTester tester) async {
await tester.pumpWidget(
ListView.builder(
itemCount: 1000,
itemBuilder: (context, index) => ListTile(
title: Text('Item $index'),
),
),
);
// Скролл для проверки производительности
await tester.drag(find.byType(ListView), Offset(0, -500));
await tester.pump();
});test('should handle large data sets efficiently', () async {
final stopwatch = Stopwatch()..start();
final products = await apiService.getProducts();
stopwatch.stop();
expect(products.length, equals(1000));
expect(stopwatch.elapsedMilliseconds, lessThan(1000));
});test('should validate JWT tokens properly', () async {
final validToken = 'valid.jwt.token';
final invalidToken = 'invalid.token';
expect(
() => apiService.validateToken(validToken),
returnsNormally,
);
expect(
() => apiService.validateToken(invalidToken),
throwsA(isA<Exception>()),
);
});test('should handle Web3 signature verification', () async {
when(mockWeb3Service.verifySignature(any, any, any))
.thenAnswer((_) async => true);
final isValid = await mockWeb3Service.verifySignature(
'0x123...',
'message',
'signature',
);
expect(isValid, isTrue);
});# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
- run: flutter test --coverage
- run: flutter analyze#!/bin/bash
# .git/hooks/pre-commit
echo "Running tests..."
flutter test
echo "Running code analysis..."
flutter analyze
echo "Checking code formatting..."
flutter format --dry-run .# Подробный вывод тестов
flutter test --reporter=expanded
# Вывод с таймаутами
flutter test --timeout=60stest('debug test', () {
debugPrint('Debug information');
// Устанавливаем breakpoint
debugger();
expect(true, isTrue);
});setUp(() {
// Инициализация перед каждым тестом
mockApiService = MockApiService();
authProvider = AuthProvider(mockApiService);
});
tearDown(() {
// Очистка после каждого теста
authProvider.dispose();
});- Группируйте связанные тесты в
group() - Используйте описательные названия тестов
- Следуйте паттерну AAA (Arrange, Act, Assert)
- Мокайте внешние зависимости
- Используйте
@GenerateMocksдля автоматической генерации - Создавайте реалистичные тестовые данные
- Используйте фабрики для создания тестовых объектов
- Избегайте хардкода в тестах
- Создавайте переиспользуемые тестовые утилиты
- Используйте специфичные матчеры
- Проверяйте как позитивные, так и негативные сценарии
- Тестируйте граничные случаи
- Не делайте тесты слишком медленными
- Используйте
setUpиtearDownдля оптимизации - Группируйте быстрые и медленные тесты
- Общее покрытие: 80%+
- Критические компоненты: 90%+
- UI компоненты: 70%+
- Unit тесты: < 1 секунды
- Widget тесты: < 5 секунд
- Интеграционные тесты: < 30 секунд
- Все тесты: < 2 минут
- Flaky тесты: 0%
- False positives: < 1%
- False negatives: 0%
-
Visual Regression Testing
- Сравнение скриншотов
- Автоматическое обнаружение изменений UI
-
Load Testing
- Тестирование производительности под нагрузкой
- Стресс-тестирование API
-
Accessibility Testing
- Автоматическая проверка доступности
- Тестирование screen readers
-
Internationalization Testing
- Тестирование локализации
- Проверка RTL языков
-
SonarQube
- Анализ качества кода
- Метрики технического долга
-
Allure Reports
- Красивые отчеты о тестах
- Интеграция с CI/CD
-
TestCafe
- E2E тестирование
- Кроссплатформенное тестирование
Если у вас возникли проблемы с тестами:
- Проверьте зависимости:
flutter doctor - Очистите проект:
flutter clean - Перегенерируйте моки:
flutter packages pub run build_runner build - Проверьте логи:
flutter test --verbose
# Проверка здоровья Flutter
flutter doctor
# Очистка проекта
flutter clean
# Установка зависимостей
flutter pub get
# Генерация моков
flutter packages pub run build_runner build
# Запуск тестов с детальным выводом
flutter test --reporter=expanded --verbose
# Анализ кода
flutter analyze
# Форматирование кода
flutter format .Дата последнего обновления: ${DateTime.now().toIso8601String()}
Версия документации: 1.0.0
Статус: ✅ Актуально