Saltar a contenido

A4.A Qué es una prueba y cómo se escribe

Para quién es esto

Es material avanzado: da por sabidas las unidades 1 a 5. La unidad 1 ya te enseñó una primera prueba; aquí aprendes a escribirlas con método.

Una prueba automática es un programa corto que ejecuta tu código con unos datos y comprueba el resultado por ti. Lo que compruebas a mano una vez, la prueba lo comprueba todas las veces, en segundos, cada vez que cambias algo. Si rompes algo sin querer, lo sabes al momento y no dentro de una semana.

La receta: preparar, actuar, comprobar

Casi todas las pruebas siguen tres pasos (Arrange, Act, Assert):

Paso Qué haces En el ejemplo
Preparar Creas los objetos y los datos Un carrito nuevo
Actuar Llamas al código que se prueba agregar('tarta', 1800, 2)
Comprobar Dices qué esperas El total es 3600

Una buena prueba comprueba una sola cosa, tiene un nombre que dice lo que debe ocurrir y no depende de otra prueba.

El marco de pruebas de Dart

Dart usa el paquete test (dart pub add --dev test). Las pruebas van en la carpeta test/, en archivos cuyo nombre acaba en _test.dart. Cada test('descripción', () { ... }) es una prueba y expect(valor, esperado) es la comprobación. Se ejecutan con dart test. El código se importa con import 'package:carrito_app/carrito.dart', donde carrito_app es el name: del pubspec.yaml.

Un ejemplo: un carrito de la compra

Primero, el código que se prueba. Los precios van en céntimos (enteros) para evitar errores de decimales:

carrito.dart

// El código que se prueba: un carrito de la compra.
// Los precios van en céntimos (números enteros) para evitar errores de redondeo con decimales.
class Carrito {
  final List<({String producto, int precio, int cantidad})> _lineas = [];

  void agregar(String producto, int precio, int cantidad) {
    if (cantidad <= 0) throw ArgumentError('la cantidad debe ser mayor que cero');
    if (precio < 0) throw ArgumentError('el precio no puede ser negativo');
    _lineas.add((producto: producto, precio: precio, cantidad: cantidad));
  }

  int get total => _lineas.fold(0, (suma, l) => suma + l.precio * l.cantidad);
  int get unidades => _lineas.fold(0, (suma, l) => suma + l.cantidad);
  bool get vacio => _lineas.isEmpty;

  /// El total después de aplicar un descuento de 0 a 100 por ciento.
  int conDescuento(int porcentaje) {
    if (porcentaje < 0 || porcentaje > 100) {
      throw ArgumentError('el porcentaje debe estar entre 0 y 100');
    }
    return total - total * porcentaje ~/ 100;
  }
}

Y sus tres primeras pruebas:

carrito_test.dart

import 'package:carrito_app/carrito.dart';
import 'package:test/test.dart';

void main() {
  late Carrito carrito;

  setUp(() {
    carrito = Carrito(); // un carrito nuevo antes de CADA prueba: así no dependen unas de otras
  });

  test('un carrito nuevo está vacío y su total es 0', () {
    expect(carrito.vacio, isTrue);
    expect(carrito.total, 0);
  });

  test('agregar un producto suma su importe', () {
    carrito.agregar('tarta', 1800, 2);
    expect(carrito.total, 3600);
    expect(carrito.unidades, 2);
  });

  test('con varios productos, el total es la suma de los importes', () {
    carrito.agregar('tarta', 1800, 2);
    carrito.agregar('galleta', 100, 12);
    expect(carrito.total, 4800);
  });
}

setUp(() { ... }) se ejecuta antes de cada prueba. Por eso el carrito se vuelve a crear: ninguna prueba hereda lo que hizo la anterior.

Al ejecutar las pruebas:

+0: un carrito nuevo está vacío y su total es 0
+1: agregar un producto suma su importe
+2: con varios productos, el total es la suma de los importes
+3: All tests passed!

+3 es el número de pruebas que han pasado hasta ese momento, y el final dice All tests passed!.

Cuando una prueba falla

Para ver un fallo de verdad, rompí el código a propósito: cambié precio × cantidad por precio + cantidad en el cálculo del total. Esto es lo que se ve:

+1 -1: agregar un producto suma su importe [E]
  Expected: <3600>
    Actual: <1802>
  test\carrito_test.dart 18:5  main.<fn>

Un buen mensaje de fallo dice qué prueba falló, qué se esperaba (3600) y qué se obtuvo (1802). Con eso suele bastar para encontrar el error sin depurar.

Una prueba que nunca ha fallado no demuestra nada

Cuando escribas una prueba, rómpela una vez a propósito (cambia el resultado esperado o el código) y comprueba que falla. Así sabes que de verdad está mirando algo.

Errores frecuentes

Error Cómo evitarlo
Pruebas que dependen unas de otras (el orden importa) Prepara de cero antes de cada prueba
Nombres como test1, test2 Describe el comportamiento: «agregar un producto suma su importe»
Comprobar muchas cosas distintas en una sola prueba Una idea por prueba: si falla, sabes qué falló
Copiar en la prueba el mismo cálculo que hace el código Escribe el resultado esperado a mano (3600), no precio * cantidad

Para practicar

Los ejercicios A4.1 y A4.4 piden escribir una función con sus pruebas y una preparación previa. Para ver la misma idea en los otros lenguajes: Pasar de uno a otro.