wandres.dev
DE LA IDEA A LA APP STORE · el panorama completo

Testing con Swift Testing

El nuevo framework de tests de Apple: @Test y #expect, más expresivo y moderno que XCTest. Prueba tu lógica con confianza y duerme tranquilo al publicar.

⏱ 13 min

Los tests no son burocracia: son la red que te deja cambiar código sin miedo. Swift Testing, el framework moderno de Apple, hace escribirlos casi agradable, con una sintaxis limpia que aprovecha las macros. Si inyectaste bien tus dependencias (lección anterior), probar es fácil.

🎯 Al terminar esta lección sabrás
  • Escribir tests con @Test y #expect.
  • Verificar condiciones y desenvolver con #require.
  • Organizar tests en suites.
  • Qué probar (y qué no).

Un test con @Test y #expect

Swift Testing usa la macro @Test para marcar una función de test y #expect para las comprobaciones:

import Testing

@Test func laSumaFunciona() {
    let resultado = 2 + 3
    #expect(resultado == 5)
}

@Test func elFiltroDejaSoloPendientes() {
    let modelo = BibliotecaModel()
    modelo.libros = [Libro(titulo: "A", leido: true),
                     Libro(titulo: "B", leido: false)]
    #expect(modelo.pendientes.count == 1)
    #expect(modelo.pendientes.first?.titulo == "B")
}

#expect toma cualquier expresión booleana. Si falla, el mensaje de error muestra los valores reales — sabes al instante qué esperaba y qué obtuvo.

Por qué #expect es mejor que los asserts antiguos

En XCTest tenías una función distinta para cada comparación (XCTAssertEqual, XCTAssertTrue, XCTAssertNil…). Swift Testing tiene una sola: #expect(cualquier condición). Y como es una macro, cuando falla descompone la expresión y te enseña el valor de cada parte. #expect(usuario.edad == 18) fallido te dice “esperaba 18, era 25”, sin que escribas nada extra. Menos API que memorizar, mejores mensajes de error. Es el tipo de mejora que hace que de verdad quieras escribir tests.

#require: desenvolver o fallar

Cuando algo debe existir para que el test siga, #require lo desenvuelve y falla limpiamente si es nil:

@Test func elPrimerLibroTieneTitulo() throws {
    let modelo = BibliotecaModel()
    modelo.libros = [Libro(titulo: "Swift", leido: false)]
    let primero = try #require(modelo.libros.first)   // falla si es nil
    #expect(primero.titulo == "Swift")
}

Parámetros y suites

Prueba muchos casos con un solo test parametrizado, y agrupa tests relacionados en una struct suite:

@Test(arguments: [1, 2, 3, 100])
func todoNumeroPositivoEsMayorQueCero(_ n: Int) {
    #expect(n > 0)
}

struct PruebasCarrito {
    @Test func empiezaVacio() {
        #expect(CarritoModel().items.isEmpty)
    }
}

El test parametrizado corre una vez por argumento — un solo test cubre cuatro casos.

Qué probar (y qué no)

Prueba la lógica

Cálculos, filtros, validaciones, transformaciones, tu capa de datos. Aquí viven los bugs y aquí rinden los tests.

🎭

Prueba con dependencias falsas

Inyecta servicios falsos (lección 6.2) para probar sin red ni base de datos real.

🤔

No pruebes el framework

No hace falta comprobar que SwiftUI dibuja un Text. Confía en Apple; prueba lo tuyo.

💡
Los tests pagan al refactorizar

La recompensa real de los tests no es “encontrar bugs hoy”: es poder cambiar el código mañana con confianza. Cuando refactorices una función o migres a una API nueva, una suite verde te dice al instante si rompiste algo. Sin tests, cada cambio es una apuesta; con ellos, es una operación segura. Esa tranquilidad, especialmente antes de publicar, no tiene precio.

⚔️ Cubre tu lógica
  1. Escribe un @Test con #expect para una función pura tuya.
  2. Prueba un método de un modelo @Observable (p. ej. un filtro).
  3. Usa #require para desenvolver un opcional en un test.
  4. Escribe un test parametrizado con @Test(arguments:).
  5. Inyecta un servicio falso y prueba un modelo sin tocar la red.