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

TipoDescripciónComando de ejecución
Prueba unitariaPrueba funciones individuales y métodos de clasesflutter test
Prueba de WidgetPrueba la UI de un solo Widgetflutter test
Prueba de integraciónPrueba el flujo completo de la aplicaciónflutter 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);
    });
  });
}

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);
  });
}

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);
    });
  });
}

Una 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.

Otras extensiones