Uso de asserts para validaciones de desarrollo en Dart

Verificaciones de lógica con assert

Un assert es una sentencia de verificación que valida si una condición es verdadera en ese preciso momento del programa. Su diseño es estratégico: se ejecutan durante el desarrollo para detectar errores lógicos de inmediato, pero el compilador las elimina por completo cuando generas una versión de producción (AOT), garantizando que no consuman recursos ni afecten el rendimiento en manos del usuario final. Debes usarlos para establecer invariantes (reglas que sabes que siempre deben cumplirse, como que un índice de un array nunca sea negativo) y nunca para validar datos que vienen de un usuario o un servidor. Si intentas usar un assert para controlar un error de entrada de red, lo que ocurrirá es que, en producción, la validación desaparecerá y tu código intentará procesar datos corruptos sin avisar, provocando fallos impredecibles o cierres inesperados. En esencia, un assert es una herramienta de seguridad para el programador, no para el usuario.

// Un ejemplo de gestión de saldos para entender la diferencia 
// entre errores de lógica (programación) y errores de uso (runtime).

class CuentaBancaria {
  double saldo;

  CuentaBancaria(this.saldo) {
    // El saldo inicial no puede ser negativo por definición de nuestro sistema.
    // Si llega aquí con saldo negativo, el programador cometió un error de lógica.
    assert(saldo >= 0, 'El saldo inicial debe ser mayor o igual a cero');
  }

  void retirar(double monto) {
    // El monto a retirar DEBE ser positivo. Si es negativo, es un bug en la lógica
    // que llama a este método. Usamos assert para atraparlo en desarrollo.
    assert(monto > 0, 'El monto a retirar debe ser un número positivo');

    if (monto > saldo) {
      // Esto NO es un assert. El usuario intentando retirar más de lo que tiene
      // es una situación normal del mundo real. Lanzamos una excepción real.
      throw ArgumentError('Saldo insuficiente para realizar la transacción');
    }

    saldo -= monto;
    print('Retiro exitoso. Nuevo saldo: \$${saldo}');
  }
}

void main() {
  print('--- Ejecución normal ---');
  var miCuenta = CuentaBancaria(100.0);
  miCuenta.retirar(30.0);

  print('\n--- Escenario de error de lógica (solo visible en modo debug) ---');
  // Para ver el error, ejecuta: dart run --enable-asserts
  // Si ejecutas solo: dart run, esta línea se ignorará en producción.
  miCuenta.retirar(-50.0); 
}

En el código anterior, fíjate en cómo diferenciamos los tipos de errores. En el constructor de CuentaBancaria, usamos assert(saldo >= 0, ...) para asegurar que no estamos creando cuentas con saldos imposibles; esto es una precondición. El segundo parámetro del assert es un mensaje que se lanza solo si la condición falla; aunque escribimos un String, Dart permite pasar cualquier objeto, y este solo se procesa (se evalúa) si el assert falla, optimizando el rendimiento.

Cuando llamamos a miCuenta.retirar(-50.0), estamos pasando un valor negativo. En un entorno de desarrollo, con --enable-asserts activo (como ocurre por defecto en dart test), el programa lanzará un AssertionError y se detendrá, permitiéndote arreglar el bug de inmediato. Sin embargo, si este código fuera a producción, el assert simplemente no existiría en el código final. Por eso, para el caso de monto > saldo, que es un error que puede ocurrir legítimamente cuando un usuario interactúa con el sistema, hemos usado throw ArgumentError. La diferencia es vital: el assert detiene al programador; la excepción detiene (o maneja) el error del usuario.

El error frecuente

Un error clásico es usar assert para validar entradas de usuario o datos externos (como una respuesta de una API).

void registrarUsuario(String email) {
  // MAL: El usuario puede escribir un email inválido en cualquier momento.
  // En producción, este assert desaparece y el error pasará desapercibido.
  assert(email.contains('@'), 'Email inválido');
  
  // BIEN: Usar una validación real que se mantenga en producción.
  if (!email.contains('@')) {
    throw FormatException('El email debe contener un @');
  }
}

Si usas el primer enfoque, tu programa funcionará perfectamente mientras haces pruebas, pero fallará silenciosamente cuando lo lances al mundo real, ya que la comprobación de la @ habrá sido eliminada por el compilador.

23

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio