Para que cada escenario tenga su propio resultado y control de reejecución en Xcode, convierte una prueba unitaria de XCTest basada en bucles en una prueba parametrizada de Swift Testing con @Test. Este recorrido utiliza un cálculo local de disposición de mosaicos para mostrar la migración y verificar cómo se presentan los resultados.
¿Por qué un bucle de XCTest no informa cada escenario por separado?
XCTest descubre métodos de prueba en una subclase de XCTestCase. Esos métodos no aceptan parámetros y sus nombres comienzan con test. Un bucle dentro de un método descubierto sigue formando parte de ese método; sus iteraciones no se convierten en pruebas descubiertas por separado. Añadir el nombre de un escenario a una aserción puede mejorar los diagnósticos, pero no crea un control de ejecución individual. Consulta Defining Test Cases and Test Methods de Apple.
El ciclo de vida sigue el mismo límite. XCTest ejecuta los métodos de configuración de la instancia antes de cada método de prueba y los de limpieza después. Por tanto, un bucle de cinco iteraciones no recibe automáticamente cinco ciclos de configuración y limpieza. Si una iteración modifica un recurso de prueba, la siguiente puede heredar ese estado, a menos que la prueba lo restablezca explícitamente. Apple documenta esta secuencia en Set Up and Tear Down State in Your Tests.
Swift Testing ofrece el resultado que se necesita aquí: una función acepta argumentos de escenarios y el ejecutor trata sus invocaciones como casos independientes. Xcode muestra sus argumentos y ofrece controles de ejecución individuales. Apple muestra este comportamiento en Go further with Swift Testing.
La distinción importa al diagnosticar fallos. Un fallo de aserción y una salida anticipada son sucesos distintos: un bucle puede seguir comprobando después de una aserción ordinaria, mientras que un error lanzado o un return explícito pueden impedir que se ejecuten las iteraciones posteriores. Separar los escenarios en casos elimina ese flujo de control compartido entre ellos. Esto no permite recuperarse de un fallo que termina el proceso.
Prepara el destino de pruebas unitarias existente
Usa Xcode 16 o posterior para este flujo de trabajo. Swift Testing se incluye en esas distribuciones de Xcode y en las cadenas de herramientas de Swift.org a partir de Swift 6.0. El proyecto Swift recomienda usar la copia integrada para el trabajo de pruebas habitual; este ejemplo no necesita dependencias de paquetes externos. Consulta Obtaining Swift Testing.
Conserva el paquete de pruebas unitarias existente. Apple admite explícitamente Swift Testing y XCTest en el mismo destino, así que la migración gradual puede empezar con un archivo Swift nuevo. En Xcode, selecciona File > New > New File From Template, elige Swift Testing Unit Test y añade el archivo al destino de pruebas unitarias existente. Si el proyecto no tiene un destino de pruebas, crea un Unit Testing Bundle desde File > New > Target. Estos pasos se documentan en Adding tests to your Xcode project.
Este recorrido prueba directamente el código de cálculo. No automatiza una ventana del escritorio. La ruta documentada por Apple para la automatización de interfaz sigue usando XCTest; aplica este enfoque parametrizado a la lógica subyacente que determina la disposición. La misma guía para añadir pruebas describe ese límite entre los marcos.
Convierte cada escenario en un registro completo de argumentos
Supongamos que una app para Mac organiza mosaicos en filas. Con cuatro columnas, cero mosaicos requieren cero filas, cuatro mosaicos llenan una fila y cinco mosaicos requieren dos filas. La tarea concreta consiste en conservar esas comprobaciones y hacer que cada escenario sea visible y ejecutable de forma independiente.
Usa un único registro que contenga el nombre del escenario, las entradas y el resultado esperado. Así, el resultado esperado permanece asociado a su entrada cuando se añaden o reordenan casos. Los cinco registros siguientes son datos de prueba ilustrativos, no observaciones de una aplicación existente.
| Escenario | Cantidad de mosaicos | Columnas | Filas esperadas | Propósito |
|---|---|---|---|---|
empty |
0 | 4 | 0 | Entrada vacía |
one-tile |
1 | 4 | 1 | Primera fila incompleta |
exact-row |
4 | 4 | 1 | Límite exacto |
wrap |
5 | 4 | 2 | Primer mosaico que supera el límite |
two-full-rows |
8 | 4 | 2 | Múltiplo exacto |
Coloca este archivo completo y autónomo en el destino de pruebas unitarias. Incluye una implementación sintética para que se pueda probar sin un módulo de aplicación ni otros archivos. No usa servicios, simulaciones, archivos ni acceso a la red.
import Testing
private struct TileLayout {
func rowCount(tileCount: Int, columns: Int) -> Int {
precondition(tileCount >= 0)
precondition(columns > 0)
let fullRows = tileCount / columns
let partialRow = tileCount % columns == 0 ? 0 : 1
return fullRows + partialRow
}
}
private struct LayoutScenario:
Codable, Sendable, CustomTestStringConvertible {
let name: String
let tileCount: Int
let columns: Int
let expectedRows: Int
var testDescription: String { name }
static let examples: [LayoutScenario] = [
.init(name: "empty", tileCount: 0,
columns: 4, expectedRows: 0),
.init(name: "one-tile", tileCount: 1,
columns: 4, expectedRows: 1),
.init(name: "exact-row", tileCount: 4,
columns: 4, expectedRows: 1),
.init(name: "wrap", tileCount: 5,
columns: 4, expectedRows: 2),
.init(name: "two-full-rows", tileCount: 8,
columns: 4, expectedRows: 2)
]
}
struct TileLayoutTests {
@Test("Tile rows", arguments: LayoutScenario.examples)
fileprivate func rowCountMatchesScenario(
_ scenario: LayoutScenario
) {
let layout = TileLayout()
let actualRows = layout.rowCount(
tileCount: scenario.tileCount,
columns: scenario.columns
)
#expect(actualRows == scenario.expectedRows)
}
}
La migración traslada la iteración a @Test(arguments:): cada elemento se convierte en el argumento de una invocación. Codable proporciona compatibilidad con la codificación para las reejecuciones selectivas, mientras que CustomTestStringConvertible proporciona una etiqueta de escenario legible. Apple documenta la parametrización en Implementing parameterized tests y las descripciones personalizadas de argumentos en Go further with Swift Testing.
El recurso de prueba se construye deliberadamente dentro de la función de prueba. Cada invocación crea su propio TileLayout; nada depende de un escenario anterior. Si migras un recurso de prueba existente, traslada su configuración de forma explícita en lugar de esperar que se ejecuten los métodos heredados de XCTest. Las suites de Swift Testing no heredan de XCTestCase; Apple asigna la configuración a un inicializador y la limpieza de clases o actores a deinit en Migrating a test from XCTest.
El cálculo acepta cantidades de mosaicos no negativas y cantidades de columnas estrictamente positivas. Sus precondiciones definen ese contrato; los valores no válidos quedan fuera de los casos de este ejemplo. Si el código real de la aplicación debe devolver un error recuperable ante una entrada no válida, cambia ese contrato de API y añade las pruebas correspondientes antes de ampliar esta tabla.
Confirma los resultados independientes y vuelve a ejecutar un caso fallido
Ejecuta la función parametrizada con el rombo que aparece junto a su declaración. Después, abre Test navigator y despliega la función para inspeccionar sus casos. Para ejecutar un caso, coloca el puntero sobre el rombo de ese caso y haz clic en su control de reproducción. Al hacer clic en el control de reproducción de la función principal, se ejecutan todos sus casos. Apple documenta ambas acciones en Running tests and interpreting results.
Para comprobar de forma controlada la presentación de resultados, cambia temporalmente expectedRows en el registro wrap de 2 a 3. Ejecuta toda la función parametrizada. El resultado esperado son cinco ejecuciones de casos, y falla la comparación wrap porque la implementación devuelve 2. Selecciona ese caso y vuelve a ejecutarlo individualmente; después, restaura el valor esperado a 2. Este es un cambio deliberado en los datos de prueba, no una prueba de un defecto de implementación.
Inspecciona los detalles Results y Arguments de la expectativa fallida para comparar el valor calculado con la entrada del escenario. Para la ejecución completa, abre View > Navigators > Reports, selecciona la acción de prueba e inspecciona su sección Tests. Estos controles de inspección se describen en la guía de resultados de pruebas de Apple.
La ejecución selectiva tiene un requisito adicional, más allá de las etiquetas legibles: el ejecutor debe poder identificar de forma determinista cada argumento. La documentación actual de Apple admite CustomTestArgumentEncodable, ciertos tipos RawRepresentable, Encodable o Identifiable con un identificador codificable. El registro Codable del ejemplo satisface la opción de codificación. Una cadena de visualización personalizada por sí sola no cumple ese requisito. Consulta Implementing parameterized tests.
El cálculo esperado para los cinco registros se comprobó localmente con la división entera y el resto de Python. Una implementación incorrecta ilustrativa que solo usa la división entera hacia abajo falla en one-tile y wrap. Se inspeccionó el archivo Swift, pero no se compiló en este entorno de investigación porque Swift no estaba disponible; la ejecución en Xcode indicada anteriormente sigue siendo el paso de verificación ejecutable.
Conserva el número de casos y el ciclo de vida previstos
Una comprobación útil de la migración compara tres cosas: el número de registros de escenarios, los casos ejecutados en la ejecución completa y si un caso seleccionado puede ejecutarse por sí solo. En este ejemplo, el número previsto es cinco. Si el número es distinto, conviene revisar la construcción de argumentos y la selección de pruebas antes de cambiar el cálculo.
Mantén juntas las expectativas emparejadas. Pasar dos colecciones distintas a arguments: genera su producto cartesiano. Cinco entradas y cinco resultados esperados producirían 25 combinaciones, en lugar de cinco escenarios emparejados. Usa colecciones separadas cuando todas las combinaciones sean pertinentes; para datos emparejados, usa registros o zip(). Apple explica la diferencia en Implementing parameterized tests.
Si usas zip(), comprueba las longitudes de las colecciones antes de construir los pares: Swift se detiene en la secuencia más corta. Un resultado esperado adicional podría quedar sin usar y no generar ningún caso. Un único registro de escenario evita ese desajuste concreto. El comportamiento de truncamiento se documenta en la referencia de Swift zip(::).
El orden de ejecución también influye en la decisión de migración. De forma predeterminada, Swift Testing ejecuta los casos en paralelo. Este ejemplo tiene argumentos inmutables y un recurso local, por lo que sus resultados no dependen del orden. Para que una función parametrizada se ejecute temporalmente en serie, añade .serialized a su atributo @Test. Si se aplica ese rasgo a una suite, también serializa las pruebas y los casos que contiene, pero no las suites que no están relacionadas. Consulta Running tests serially or in parallel.
La ejecución en serie no crea un estado nuevo ni establece un contrato de dependencia entre escenarios. Si un caso solo funciona después de que otro haya cambiado algo, rediseña su configuración para que pueda ejecutarse de forma independiente. Si los pasos forman intencionadamente una sola operación continua, consérvalos en una prueba y parametriza operaciones completas.
Usa las aserciones de Swift Testing en todo este ejemplo migrado. Si reutilizas funciones auxiliares que contienen aserciones de XCTest, comprueba el comportamiento de interoperabilidad de la cadena de herramientas: la guía de migración actual de Apple documenta modos y valores predeterminados que varían según la cadena de herramientas y la versión de herramientas del paquete. Mantener este archivo íntegramente en Testing evita esa dependencia. Consulta Migrating a test from XCTest.
Si hay que aplazar la migración, la alternativa compatible en XCTest consiste en un método de prueba con nombre y sin parámetros por escenario, con funciones auxiliares compartidas para el cálculo y las aserciones, según convenga. Apple recomienda una prueba por escenario en su guía para añadir pruebas. Así se conservan resultados de métodos independientes sin necesidad de parametrización en Swift Testing.
La conversión está completa cuando cada escenario original tiene un caso ejecutado correspondiente, su recurso de prueba parte del estado previsto y un caso seleccionado puede ejecutarse correctamente por sí solo. Conserva las entradas límite y los resultados esperados durante ese cambio: los informes independientes son útiles porque facilitan localizar, diagnosticar y repetir esas comprobaciones existentes.
Fuentes
- Defining Test Cases and Test Methods — Apple
- Set Up and Tear Down State in Your Tests — Apple
- Go further with Swift Testing — Apple
- Obtaining Swift Testing — proyecto Swift
- Adding tests to your Xcode project — Apple
- Implementing parameterized tests — Apple
- Migrating a test from XCTest — Apple
- Running tests and interpreting results — Apple
- zip(::) — Apple
- Running tests serially or in parallel — Apple