# Integration Testing en Flutter
El integration testing prueba cómo funcionan juntos múltiples componentes de la aplicación, incluyendo servicios
externos, bases de datos y la UI completa.
## ¿Qué es Integration Testing?
- Prueba flujos completos end-to-end
- Se ejecuta en dispositivos reales o emuladores
- Más lento pero más cercano al uso real
- Detecta problemas de integración entre componentes
## Configuración
```yaml
# pubspec.yaml
dev_dependencies:
integration_test:
sdk: flutter
flutter_test:
sdk: flutter
```
```dart
// test_driver/integration_test.dart
import 'package:integration_test/integration_test_driver.dart';
Future main() => integrationDriver();
```
## Estructura de Archivos
```
my_app/
├── integration_test/
│ ├── app_test.dart
│ ├── login_flow_test.dart
│ └── checkout_flow_test.dart
├── test_driver/
│ └── integration_test.dart
└── lib/
└── ...
```
## Ejemplo Básico
```dart
// integration_test/app_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('complete app flow', (tester) async {
app.main();
await tester.pumpAndSettle();
// Tu test aquí
expect(find.text('Welcome'), findsOneWidget);
});
}
```
## Ejecutar Integration Tests
```mermaid
graph TD
subgraph Host Machine
Driver[Integration Driver]
end
subgraph Device/Emulator
App[Flutter App]
Test[Test Code]
end
Driver -->|Controla| Test
Test -->|Interactúa con| App
App -->|Retorna resultados| Driver
style Host Machine fill:#e3f2fd,stroke:#2196f3
style Device/Emulator fill:#f1f8e9,stroke:#8bc34a
```
```bash
# En dispositivo/emulador
flutter test integration_test/app_test.dart
# Con driver
flutter drive \
--driver=test_driver/integration_test.dart \
--target=integration_test/app_test.dart
# En dispositivo específico
flutter test integration_test/app_test.dart -d
```
## Ejemplo Completo: Login Flow
```dart
// integration_test/login_flow_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('Login Flow', () {
testWidgets('successful login navigates to home', (tester) async {
app.main();
await tester.pumpAndSettle();
// 1. Verificar pantalla de login
expect(find.text('Login'), findsOneWidget);
expect(find.byType(TextField), findsNWidgets(2));
// 2. Ingresar credenciales
await tester.enterText(
find.byKey(Key('email-field')),
'[email protected]',
);
await tester.enterText(
find.byKey(Key('password-field')),
'password123',
);
await tester.pumpAndSettle();
// 3. Tap en botón de login
await tester.tap(find.byKey(Key('login-button')));
await tester.pumpAndSettle(Duration(seconds: 5)); // Esperar navegación y carga
// 4. Verificar que navegó a home
expect(find.byType(HomePage), findsOneWidget);
expect(find.text('Welcome, Test User'), findsOneWidget);
});
testWidgets('invalid credentials shows error', (tester) async {
app.main();
await tester.pumpAndSettle();
// Ingresar credenciales inválidas
await tester.enterText(
find.byKey(Key('email-field')),
'[email protected]',
);
await tester.enterText(
find.byKey(Key('password-field')),
'wrongpassword',
);
// Submit
await tester.tap(find.byKey(Key('login-button')));
await tester.pumpAndSettle(Duration(seconds: 3));
// Verificar error
expect(find.text('Invalid credentials'), findsOneWidget);
expect(find.byType(LoginPage), findsOneWidget); // Sigue en login
});
});
}
```
## Testear API Calls
```dart
testWidgets('fetches and displays user data', (tester) async {
app.main();
await tester.pumpAndSettle();
// Navegar a la pantalla de perfil
await tester.tap(find.byIcon(Icons.person));
await tester.pumpAndSettle();
// Esperar a que cargue loading indicator
expect(find.byType(CircularProgressIndicator), findsOneWidget);
// Esperar a que cargue la data real
await tester.pumpAndSettle(Duration(seconds: 5));
// Verificar datos cargados
expect(find.text('John Doe'), findsOneWidget);
expect(find.text('[email protected]'), findsOneWidget);
});
```
## Testear Scrolling y Lists
```dart
testWidgets('scrolls through product list', (tester) async {
app.main();
await tester.pumpAndSettle();
// Verificar primer item visible
expect(find.text('Product 1'), findsOneWidget);
expect(find.text('Product 20'), findsNothing);
// Scroll hasta el final
final listFinder = find.byType(ListView);
await tester.fling(listFinder, Offset(0, -3000), 10000);
await tester.pumpAndSettle();
// Verificar último item visible
expect(find.text('Product 1'), findsNothing);
expect(find.text('Product 20'), findsOneWidget);
// Tap en producto
await tester.tap(find.text('Product 20'));
await tester.pumpAndSettle();
// Verificar navegación a detalle
expect(find.byType(ProductDetailPage), findsOneWidget);
});
```
## Testear Formularios Complejos
```dart
testWidgets('checkout flow completes successfully', (tester) async {
app.main();
await tester.pumpAndSettle();
// 1. Agregar productos al carrito
await tester.tap(find.text('Add to Cart').first);
await tester.pumpAndSettle();
// 2. Ir al carrito
await tester.tap(find.byIcon(Icons.shopping_cart));
await tester.pumpAndSettle();
expect(find.text('Cart'), findsOneWidget);
expect(find.byType(CartItem), findsOneWidget);
// 3. Proceder al checkout
await tester.tap(find.text('Checkout'));
await tester.pumpAndSettle();
// 4. Llenar información de envío
await tester.enterText(find.byKey(Key('address-field')), '123 Main St');
await tester.enterText(find.byKey(Key('city-field')), 'New York');
await tester.enterText(find.byKey(Key('zip-field')), '10001');
// 5. Continuar a pago
await tester.tap(find.text('Continue to Payment'));
await tester.pumpAndSettle();
// 6. Ingresar información de pago
await tester.enterText(find.byKey(Key('card-number')), '4242424242424242');
await tester.enterText(find.byKey(Key('card-expiry')), '12/25');
await tester.enterText(find.byKey(Key('card-cvc')), '123');
// 7. Confirmar orden
await tester.tap(find.text('Place Order'));
await tester.pumpAndSettle(Duration(seconds: 5));
// 8. Verificar confirmación
expect(find.text('Order Confirmed'), findsOneWidget);
expect(find.textContaining('Order #'), findsOneWidget);
});
```
## Mock Services para Integration Tests
```dart
// test/mocks/mock_api_client.dart
class MockApiClient implements ApiClient {
@override
Future login(String email, String password) async {
await Future.delayed(Duration(seconds: 1)); // Simular latencia
if (email == '[email protected]' && password == 'password123') {
return User(id: '1', name: 'Test User', email: email);
}
throw Exception('Invalid credentials');
}
@override
Future
- > getProducts() async {
await Future.delayed(Duration(milliseconds: 500));
return List.generate(
20,
(i) => Product(id: '$i', name: 'Product ${i + 1}', price: 10.0 * (i + 1)),
);
}
}
// integration_test/app_test.dart con mocks
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
setUp(() {
// Inyectar mock
getIt.registerSingleton
(Rápidos, Aislados)] Widget[Widget Tests: 20%
(UI, Componentes)] Integration[Integration Tests: 10%
(Lentos, End-to-End)] Unit --> Widget Widget --> Integration style Unit fill:#c8e6c9,stroke:#4caf50,stroke-width:2px style Widget fill:#fff9c4,stroke:#fbc02d,stroke-width:2px style Integration fill:#ffcdd2,stroke:#e57373,stroke-width:2px ``` **Regla 70-20-10:** - 70% Unit Tests - 20% Widget Tests - 10% Integration Tests
Este apartado profundiza en los conceptos clave, proporcionando ejemplos prácticos y mejores prácticas para su aplicación en proyectos reales.
Al comprender estos detalles, podrás diseñar soluciones más robustas, mantenibles y escalables en tus aplicaciones Flutter y Dart.