Yeda AI Tips · #092

English

Simula la red, la persistencia, los relojes — y el estado de permisos

Simulas la red y la base de datos en tus tests. Pero el diálogo de permisos es la rama sin probar que llega rota a producción. Las llamadas de red, las escrituras a disco y el reloj del sistema se reemplazan con dobles de prueba porque son obviamente difíciles de controlar. El estado de permisos — cámara, micrófono, ubicación, notificaciones — es igual de externo, y sin embargo la mayoría de los códigos lo leen directo del sistema operativo. El resultado: la ruta de "concedido" se ejercita todos los días en tu propio dispositivo, y la ruta de "denegado" corre por primera vez en el teléfono de un usuario.

Cuatro sistemas externos, no tres

La lista clásica de cosas que simular en tests tiene tres entradas. Debería tener cuatro:

DependenciaPor qué rompe los testsQué devuelve el fake
RedLenta, inestable, necesita un servidorRespuestas predefinidas, fallos inyectados
PersistenciaFiltra estado entre testsAlmacén en memoria, se reinicia por test
RelojEl tiempo avanza; los tests se vuelven inestablesUn instante fijo o avanzado manualmente
Estado de permisosLo fija el SO, un diálogo por instalacióngranted, denied o notDetermined — tú eliges

Los tres primeros son práctica estándar — Martin Fowler catalogó el vocabulario (dummy, fake, stub, spy, mock) hace dos décadas, y java.time.Clock incluso trae fixed() específicamente para pruebas, para que los tests no dependan del reloj actual. El estado de permisos merece el mismo trato, porque tiene la misma forma: global, mutable, controlado por algo fuera de tu proceso.

El mecanismo: pon un protocolo delante del diálogo

Nunca llames a la API de permisos del SO directamente desde el código de la funcionalidad. Define una interfaz pequeña e inyéctala:

protocol CameraPermission {
    var status: AVAuthorizationStatus { get }   // .authorized, .denied,
                                                // .restricted, .notDetermined
    func request() async -> Bool
}

struct LivePermission: CameraPermission { /* wraps AVCaptureDevice */ }

struct FakePermission: CameraPermission {       // test double
    var status: AVAuthorizationStatus
    var grantOnRequest = false
    func request() async -> Bool { grantOnRequest }
}

En plataformas de Apple, los estados reales vienen de AVCaptureDevice.authorizationStatus(for:), que devuelve exactamente cuatro valores: .notDetermined, .restricted, .denied, .authorized. En Android, la costura equivalente envuelve ContextCompat.checkSelfPermission() y el launcher de solicitud. En ambos casos, el fake convierte cada estado en un simple argumento de constructor — sin ajustes del simulador, sin "resetear privacidad", sin diálogo que tocar.

Por qué importa más con tests generados por IA

Un agente de código solo puede generar tests para ramas que puede alcanzar. Si el estado de permisos viene de una llamada estática al SO, la rama de denegado es imposible de probar y el agente la salta en silencio. Pon un protocolo delante y el caso denegado se vuelve una línea de setup — FakePermission(status: .denied) — que un agente enumerará con gusto junto a concedido y aún-no-pedido. La rama que antes llegaba rota a producción por fin tiene cobertura, y es determinista en CI.

Movidas de usuario avanzado

Recursos

Read this article in English

¿Construyendo una funcionalidad con IA? Yeda AI diseña, audita y entrega sistemas LLM de producción.

Habla con nosotros · Lee el blog