Pruebas de mutación en Swift: el arma secreta para un código perfecto

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Tus pruebas mienten, y ni siquiera lo sabes

\n\n

Imagina esta situación: escribiste 200 pruebas unitarias, la cobertura de código es del 95%, el CI está verde, el gerente está feliz. Lanzas la versión, y una semana después la producción se cae debido a un error que las pruebas deberían haber detectado. ¿Te suena familiar? Bienvenido al mundo de la falsa confianza, donde mutation testing (pruebas de mutación) actúa como un espejo honesto para tu código Swift.

\n\n

En este artículo, analizaremos qué son las pruebas de mutación en Swift, cómo funcionan en la práctica y por qué todo desarrollador iOS debería agregarlas a su arsenal. Sin teoría aburrida, solo código, ejemplos y casos reales.

\n\n

¿Qué son las pruebas de mutación y para qué sirven?

\n\n

Las pruebas de mutación (mutation testing) son un método para evaluar la calidad de las pruebas mediante la introducción de pequeños cambios (mutaciones) en el código fuente. Estas versiones modificadas se llaman mutantes. Si las pruebas no detectan al mutante (es decir, no fallan), entonces tienes una cobertura de prueba débil, y ese mutante sobrevive.

\n\n

¿Por qué es importante? Las métricas tradicionales como la cobertura de código (code coverage) solo muestran qué porcentaje del código se ejecutó durante las pruebas. Pero no responden a la pregunta principal: ¿las pruebas verifican la corrección de la lógica? Las pruebas de mutación dan la respuesta: si accidentalmente cambias la condición if a > b por if a < b, ¿tus pruebas lo notarán?

\n\n

Cómo funcionan las pruebas de mutación en Swift: explicación sencilla

\n\n

El proceso es el siguiente:

\n\n
    \n
  1. Código original — tu archivo Swift de trabajo.
  2. \n
  3. Generación de mutantes — el programa crea automáticamente múltiples versiones del código con un pequeño error (por ejemplo, cambia + por -).
  4. \n
  5. Ejecución de pruebas — para cada mutante se ejecuta el conjunto completo de pruebas unitarias.
  6. \n
  7. Análisis de resultados — si las pruebas fallan, el mutante está «muerto». Si las pruebas pasan, el mutante «sobrevive», y eso es una bandera roja.
  8. \n
\n\n

El porcentaje de mutantes muertos se llama mutation score. Cuanto más alto sea, más confiables son tus pruebas.

\n\n

Ejemplo práctico: escribiendo código y pruebas en Swift

\n\n

Imaginemos que estamos escribiendo una calculadora de descuentos simple para una tienda en línea en Swift. Aquí está nuestro código de producción:

\n\n
// DiscountCalculator.swift\nstruct DiscountCalculator {\n    func calculateDiscount(price: Double, isPremiumUser: Bool) -> Double {\n        var discount: Double = 0.0\n        \n        if price > 100 {\n            discount += 0.1 // 10% de descuento base\n        }\n        \n        if isPremiumUser {\n            discount += 0.05 // 5% adicional para premium\n        }\n        \n        return price * (1 - discount)\n    }\n}\n
\n\n

Parece lógico. Escribamos las pruebas unitarias:

\n\n
// DiscountCalculatorTests.swift\nimport XCTest\n\nfinal class DiscountCalculatorTests: XCTestCase {\n    let calculator = DiscountCalculator()\n    \n    func testDiscountForRegularUserLowPrice() {\n        let result = calculator.calculateDiscount(price: 50, isPremiumUser: false)\n        XCTAssertEqual(result, 50.0, accuracy: 0.01)\n    }\n    \n    func testDiscountForRegularUserHighPrice() {\n        let result = calculator.calculateDiscount(price: 200, isPremiumUser: false)\n        XCTAssertEqual(result, 180.0, accuracy: 0.01)\n    }\n    \n    func testDiscountForPremiumUserLowPrice() {\n        let result = calculator.calculateDiscount(price: 50, isPremiumUser: true)\n        XCTAssertEqual(result, 47.5, accuracy: 0.01)\n    }\n    \n    func testDiscountForPremiumUserHighPrice() {\n        let result = calculator.calculateDiscount(price: 200, isPremiumUser: true)\n        XCTAssertEqual(result, 170.0, accuracy: 0.01)\n    }\n}\n
\n\n

Las pruebas pasan. La cobertura es del 100%. Pero veamos qué dicen las pruebas de mutación.

\n\

Blogs

Book Recommendations