Pruebas de Flutter
Esta sección presentará los tres tipos de pruebas en Flutter: pruebas unitarias, pruebas de Widget y pruebas de integración.
Resumen de tipos de pruebas
| Tipo | Descripción | Comando de ejecución |
|---|---|---|
| Prueba unitaria | Prueba funciones individuales y métodos de clases | flutter test |
| Prueba de Widget | Prueba la UI de un solo Widget | flutter test |
| Prueba de integración | Prueba el flujo completo de la aplicación | flutter test integration_test/ |
Pruebas unitarias
Las pruebas unitarias se utilizan para probar código de lógica de negocio.
Ejemplo: Ejemplo de prueba unitaria
// math_utils.dart - código a probar
class MathUtils {
static int add(int a, int b) => a + b;
static int subtract(int a, int b) => a - b;
static int multiply(int a, int b) => a * b;
static double divide(int a, int b) {
if (b == 0) throw ArgumentError('No se puede dividir por cero');
return a / b;
}
static int factorial(int n) {
if (n < 0) throw ArgumentError('Los números negativos no tienen factorial');
if (n == 0 || n == 1) return 1;
return n * factorial(n - 1);
}
}
// math_utils_test.dart - archivo de prueba
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/math_utils.dart';
void main() {
group('MathUtils', () {
test('add suma dos números', () {
expect(MathUtils.add(2, 3), equals(5));
});
test('subtract resta dos números', () {
expect(MathUtils.subtract(5, 3), equals(2));
});
test('multiply multiplica dos números', () {
expect(MathUtils.multiply(4, 5), equals(20));
});
test('divide divide dos números', () {
expect(MathUtils.divide(10, 2), equals(5));
});
test('divide lanza una excepción al dividir por cero', () {
expect(() => MathUtils.divide(10, 0), throwsArgumentError);
});
test('factorial calcula el factorial', () {
expect(MathUtils.factorial(5), equals(120));
expect(MathUtils.factorial(0), equals(1));
});
test('factorial lanza una excepción para números negativos', () {
expect(() => MathUtils.factorial(-1), throwsArgumentError);
});
});
}
class MathUtils {
static int add(int a, int b) => a + b;
static int subtract(int a, int b) => a - b;
static int multiply(int a, int b) => a * b;
static double divide(int a, int b) {
if (b == 0) throw ArgumentError('No se puede dividir por cero');
return a / b;
}
static int factorial(int n) {
if (n < 0) throw ArgumentError('Los números negativos no tienen factorial');
if (n == 0 || n == 1) return 1;
return n * factorial(n - 1);
}
}
// math_utils_test.dart - archivo de prueba
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/math_utils.dart';
void main() {
group('MathUtils', () {
test('add suma dos números', () {
expect(MathUtils.add(2, 3), equals(5));
});
test('subtract resta dos números', () {
expect(MathUtils.subtract(5, 3), equals(2));
});
test('multiply multiplica dos números', () {
expect(MathUtils.multiply(4, 5), equals(20));
});
test('divide divide dos números', () {
expect(MathUtils.divide(10, 2), equals(5));
});
test('divide lanza una excepción al dividir por cero', () {
expect(() => MathUtils.divide(10, 0), throwsArgumentError);
});
test('factorial calcula el factorial', () {
expect(MathUtils.factorial(5), equals(120));
expect(MathUtils.factorial(0), equals(1));
});
test('factorial lanza una excepción para números negativos', () {
expect(() => MathUtils.factorial(-1), throwsArgumentError);
});
});
}
Pruebas de Widget
Las pruebas de Widget se utilizan para probar componentes de UI.
Ejemplo: Prueba de Widget
// counter_widget.dart - Widget a probar
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Contador: $_count', key: const Key('counter_text')),
ElevatedButton(
key: const Key('increment_button'),
onPressed: _increment,
child: const Text('Incrementar'),
),
],
);
}
}
// counter_widget_test.dart - archivo de prueba
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/counter_widget.dart';
void main() {
testWidgets('CounterWidget muestra el contador inicial', (WidgetTester tester) async {
// Construir el Widget
await tester.pumpWidget(const MaterialApp(
home: CounterWidget(),
));
// Verificar la visualización del contador inicial
expect(find.text('Contador: 0'), findsOneWidget);
});
testWidgets('CounterWidget incrementa el contador al hacer clic en el botón', (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp(
home: CounterWidget(),
));
// Encontrar y hacer clic en el botón
await tester.tap(find.byKey(const Key('increment_button')));
await tester.pump();
// Verificar que el contador se ha actualizado
expect(find.text('Contador: 1'), findsOneWidget);
// Hacer clic de nuevo
await tester.tap(find.byKey(const Key('increment_button')));
await tester.pump();
expect(find.text('Contador: 2'), findsOneWidget);
});
}
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Contador: $_count', key: const Key('counter_text')),
ElevatedButton(
key: const Key('increment_button'),
onPressed: _increment,
child: const Text('Incrementar'),
),
],
);
}
}
// counter_widget_test.dart - archivo de prueba
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/counter_widget.dart';
void main() {
testWidgets('CounterWidget muestra el contador inicial', (WidgetTester tester) async {
// Construir el Widget
await tester.pumpWidget(const MaterialApp(
home: CounterWidget(),
));
// Verificar la visualización del contador inicial
expect(find.text('Contador: 0'), findsOneWidget);
});
testWidgets('CounterWidget incrementa el contador al hacer clic en el botón', (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp(
home: CounterWidget(),
));
// Encontrar y hacer clic en el botón
await tester.tap(find.byKey(const Key('increment_button')));
await tester.pump();
// Verificar que el contador se ha actualizado
expect(find.text('Contador: 1'), findsOneWidget);
// Hacer clic de nuevo
await tester.tap(find.byKey(const Key('increment_button')));
await tester.pump();
expect(find.text('Contador: 2'), findsOneWidget);
});
}
Pruebas de integración
Las pruebas de integración se utilizan para probar el flujo funcional completo de la aplicación.
Ejemplo: Prueba de integración
// integration_test/app_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';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('Prueba de integración de la aplicación', () {
testWidgets('Prueba de flujo de usuario completo', (WidgetTester tester) async {
// Iniciar la aplicación
await tester.pumpWidget(const MyApp());
await tester.pumpAndSettle();
// Verificar que se muestra la página de inicio
expect(find.text('Inicio'), findsOneWidget);
// Hacer clic para ir a la página de detalles
await tester.tap(find.text('Detalles'));
await tester.pumpAndSettle();
// Verificar que se muestra la página de detalles
expect(find.text('Página de detalles'), findsOneWidget);
// Hacer clic en volver
await tester.tap(find.byIcon(Icons.arrow_back));
await tester.pumpAndSettle();
// Verificar el regreso a la página de inicio
expect(find.text('Inicio'), findsOneWidget);
});
});
}
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';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('Prueba de integración de la aplicación', () {
testWidgets('Prueba de flujo de usuario completo', (WidgetTester tester) async {
// Iniciar la aplicación
await tester.pumpWidget(const MyApp());
await tester.pumpAndSettle();
// Verificar que se muestra la página de inicio
expect(find.text('Inicio'), findsOneWidget);
// Hacer clic para ir a la página de detalles
await tester.tap(find.text('Detalles'));
await tester.pumpAndSettle();
// Verificar que se muestra la página de detalles
expect(find.text('Página de detalles'), findsOneWidget);
// Hacer clic en volver
await tester.tap(find.byIcon(Icons.arrow_back));
await tester.pumpAndSettle();
// Verificar el regreso a la página de inicio
expect(find.text('Inicio'), findsOneWidget);
});
});
}
Otras extensionesUna buena cobertura de pruebas puede mejorar la calidad del código; se recomienda escribir pruebas unitarias al menos para la lógica de negocio principal.