Cuando ya has acumulado cierta cantidad de código de pruebas, surge una nueva duda: «¿Con qué combinación debería ejecutar estas pruebas?»
Ejecutarlas todas tarda unos 20 minutos en CI (Continuous Integration, integración continua). Ejecutar solo algunas obliga a activar y desactivar opciones manualmente cada vez.
Además, puede que quieras ejecutar pruebas de UI en entornos en coreano e inglés y no encuentres una forma adecuada de hacerlo.
En resumen, Apple creó los planes de pruebas (Test Plan) precisamente para resolver este problema.
Planes de pruebas introducidos en Xcode 11separa en archivos las «pruebas que se ejecutarán» y «las condiciones con las que se ejecutarán».
Hoy repasaremos de una vez la estructura de los planes de pruebas, su configuración práctica y su integración con CI.
Si todavía no te resulta familiar escribir código de pruebas, puedes empezar por Cómo escribir tu primera prueba con XCTest y Given-When-Then.
¿Qué es un plan de pruebas?
Un plan de pruebas es un archivo con extensión .xctestplan. Su contenido es JSON legible y, tras añadirlo al proyecto, se referencia desde un esquema (Scheme).
En un archivo se incluyen dos elementos.
- Pruebas que se ejecutarán: qué objetivos, suites y funciones de prueba incluir o excluir. En Xcode 16 y versiones posteriores también puedes usar etiquetas de Swift Testing como condiciones
- Configuración (Configuration): bajo qué condiciones se ejecutarán esas pruebas
Lo importante es que se trata de un archivo. Puedes confirmar .xctestplan en Git, compartir las mismas condiciones con el equipo y someter los cambios de configuración a revisión de código.
En un equipo, es más seguro versionar juntos el plan y el esquema compartido que lo referencia.
¿Por qué no basta con la configuración del esquema?
Antes de los planes de pruebas, todas las condiciones de ejecución estaban dentro de la acción Test del esquema. Esto tenía limitaciones estructurales.
Cada esquema solo tenía una configuración de la acción Test. Para separar la «validación rápida» de la «validación nocturna completa», había que duplicar el esquema.
Al duplicar un esquema también se copian las configuraciones de compilación, ejecución y perfil. Es como copiarlo todo para cambiar una sola condición de prueba.
Los planes de pruebas invierten esta relación. Puedes asociar varios planes de pruebas a un solo esquema, y cada plan puede tener varias configuraciones.
Crear un plan de pruebas
Según Guía actual de Apple, Xcode crea un plan predeterminado que incluye todas las pruebas de los objetivos de prueba compilados por el esquema.
- Abre el plan predeterminado en Product > Scheme > Edit Test Plan y guárdalo
- En la pestaña Tests, elige los objetivos de ejecución por etiqueta, suite o función
- En la pestaña Configurations, añade la configuración compartida y las necesarias
Para crear más planes, usa Product > Test Plan > New Test Plan. En esquemas antiguos que aún no usan planes de pruebas, el editor puede mostrar Convert to use Test Plans. Es Flujo de conversión de configuraciones existentes introducido en Xcode 11.
Si has asociado varios planes, establece uno como predeterminado en Product > Test Plan > Manage Test Plans. Este es el que se usa cuando no se especifica otro.
Activa el plan que ejecutarás en Product > Test Plan. Después, al ejecutar Command + U, es decir, Product > Test, el plan activo se ejecuta una vez por configuración. Conviene distinguir el plan predeterminado del activo.
Relación entre la configuración compartida y las configuraciones
Al abrir el editor del plan aparecen las pestañas Tests y Configurations. La clave está en Configurations.
Esta pestaña tiene dos niveles, uno encima del otro.
- Shared Settings (configuración compartida): valores predeterminados heredados por todas las configuraciones
- Configuración individual: variante que sobrescribe solo los elementos necesarios de la configuración compartida
Es parecido a la herencia de CSS. Las condiciones comunes se definen en un lugar y solo se especifican aparte los elementos que cambian por configuración.
Los elementos sobrescritos aparecen en negrita en el editor, por lo que se ve de inmediato «qué es diferente aquí».
Hay muchas opciones configurables; estas son las más habituales.
| Elemento | Qué se determina |
|---|---|
| Arguments / Environment Variables | Argumentos de ejecución y variables de entorno |
| Application Language / Region | Idioma y región en los que se ejecuta la app |
| Code Coverage | Si se recopila cobertura y qué objetivos se cubren |
| Execution Order | Si se ejecutan en orden alfabético o se mezclan aleatoriamente en cada ejecución |
| Test Repetition Mode | Modo de repetición de las pruebas |
| Test Timeouts | Tiempo máximo permitido para ejecutar una prueba |
| Runtime Sanitization | Address·Thread·Undefined Behavior Sanitizer |
| Memory Management | Malloc Scribble, Malloc Guard Edges, Zombie Objects |
| Automatic Screen Capture | Si se adjuntan capturas de pantalla automáticamente cuando falla |
Lo común va en la configuración compartida; las diferencias, en cada configuración. Si sigues este principio, el mantenimiento no se descontrolará aunque aumenten los planes.
Ejemplo práctico de separación de configuraciones
La configuración por idioma es la más habitual. En una app multilingüe, es frecuente que el diseño se rompa según el idioma; al separar las configuraciones, puedes repetir automáticamente el mismo código de pruebas de UI para cada idioma.
- Configuración compartida: activar la cobertura y guardar capturas cuando haya fallos
- Configuración A «Korean»: establecer Application Language en coreano
- Configuración B «English»: establecer Application Language en inglés
- Configuración C «RTL Pseudolanguage»: para validar idiomas escritos de derecha a izquierda
Al ejecutar este plan una vez, Las pruebas seleccionadas se ejecutan una vez en cada configuración y el informe de resultados también se muestra separado por configuración. Así queda claro en qué idioma falló.
La comprobación de memoria funciona igual. Según la documentación de Apple, Address Sanitizer puede usar entre 2 y 3 veces más memoria y ralentizar el código entre 2 y 5 veces, debes tener en cuenta el coste de ejecución.
En su lugar, desactiva los sanitizers en la configuración habitual y crea configuraciones de diagnóstico independientes. Por ejemplo, separa las configuraciones para Address Sanitizer y Thread Sanitizer según su propósito y ejecútalas en la compilación nocturna.
Cobertura, orden aleatorio y repetición
Veamos tres opciones especialmente útiles dentro de una configuración.
La cobertura de código no consiste solo en activarla: primero debes decidir qué medir. Si incluyes también las bibliotecas dependientes en una sola cifra, puede resultar difícil interpretar los cambios del código de la app.
Selecciona «some targets» y especifica solo el objetivo de nuestra app para obtener una cifra significativa.
En Execution Order puedes elegir Alphabetical o Random. Sin embargo, no debes generalizar que Alphabetical es el valor predeterminado de todas las pruebas.
Si eliges Random en un plan de XCTest, el orden cambia en cada ejecución. En cambio, Swift Testing ejecuta las funciones de prueba en paralelo de forma predeterminada y también aleatoriza su orden. Hay que distinguir el comportamiento de ambos frameworks.
Si una prueba falla al cambiar el orden, probablemente dependía del estado dejado por la prueba anterior. Es una señal útil de acoplamiento oculto entre pruebas. Esta independencia también es fundamental en El principio FIRST de las buenas pruebas unitarias.
Test Repetition sirve para Opción añadida en Xcode 13, y permite elegir el modo de repetición de la siguiente forma.
| Modo | Comportamiento | Uso |
|---|---|---|
| Up Until Maximum Repetitions | Repetir independientemente del resultado hasta alcanzar el máximo especificado | Medir la tasa de reproducción de fallos intermitentes |
Repetir hasta que falle (-run-tests-until-failure) |
Repetir hasta que se produzca un fallo | Rastrear pruebas que fallan ocasionalmente |
| Retry on Failure | Si falla, reintentar hasta alcanzar el número especificado | Proteger la tasa de éxito en CI de pruebas de UI inestables |
Según Notas de la versión de Xcode 13, Maximum Test Repetitions debe ser un entero positivo. Como la documentación oficial no confirma un «valor predeterminado de 3», lo correcto es comprobar directamente el valor guardado en el plan.
En la línea de comandos, establece el número con -test-iterations. Puedes combinarlo con -run-tests-until-failure o -retry-tests-on-failure. Esta configuración de repetición de la línea de comandos tiene prioridad sobre la del plan.
Retry on Failure es práctico, pero conviene usarlo con cuidado. Si una prueba pasa gracias al reintento, conservarás la inestabilidad sin resolver.
Actívalo como medida temporal, pero es mejor investigar por separado por qué la prueba es inestable.
Usar planes de pruebas en CI
Los planes de pruebas también se pueden usar directamente desde la línea de comandos. Primero, comprueba el plan asociado al esquema con xcodebuild -scheme MyApp -showTestPlans. A -testPlan debes pasarle el nombre del plan, no la ruta del archivo.
xcodebuild test \
-project MyApp.xcodeproj \
-scheme MyApp \
-testPlan Smoke \
-destination 'platform=iOS Simulator,name=iPhone 16'
Puedes mantener el mismo esquema y cambiar solo el nombre del plan para dividir el alcance de ejecución de los trabajos de CI. Por ejemplo:
- En cada PR (Pull Request):
-testPlan Smoke— centrado en las pruebas unitarias esenciales - Al fusionar en la rama principal:
-testPlan Regression— todas las pruebas unitarias y de UI - Programación nocturna:
-testPlan Nightly— incluye sanitizers y configuraciones multilingües
También puedes dividirlo aún más por configuración.
Como Ejemplo actual de línea de comandos de Apple, --only-test-configuration ejecuta solo las configuraciones especificadas. En cambio, --skip-test-configuration ejecuta todas excepto las configuraciones indicadas. Ambas opciones usan dos guiones.
Si un plan tiene cinco configuraciones de idioma, puedes crear cinco trabajos en el sistema de CI y asignar una configuración a cada uno. Sin embargo, esta distribución en paralelo la realiza el sistema de CI; el plan de pruebas no divide máquinas automáticamente ni reduce el tiempo de ejecución.
xcodebuild test \
-scheme MyApp \
-testPlan Localization \
--only-test-configuration Korean \
-destination 'platform=iOS Simulator,name=iPhone 16'
Preguntas frecuentes
P. ¿Se debe subir el archivo del plan de pruebas a Git?
R. Si quieres compartir las mismas condiciones de ejecución con el equipo, conviene confirmarlo en el repositorio. Subirlo a Git no es un requisito de Xcode. Gestiona conjuntamente el plan y el esquema compartido que lo referencia.
Sin embargo, al ser JSON, habrá conflictos si varias personas lo modifican a la vez. Separar los archivos por propósito del plan reduce su frecuencia.
P. Si creo varias configuraciones, ¿el tiempo se multiplica?
R. Sí. Las pruebas se ejecutan tantas veces como configuraciones haya.
Por eso conviene sacar las configuraciones pesadas, como las multilingües y las de sanitizers, del plan habitual y ejecutarlas en un plan nocturno.
P. ¿Puedo incluir pruebas unitarias y de UI en un mismo plan?
R. Sí. Pero las pruebas unitarias tardan segundos y las de UI minutos, así que la velocidad de retroalimentación difiere mucho.
Para el flujo de desarrollo suele funcionar mejor separar un plan rápido y otro lento.
P. ¿Las pruebas escritas con Swift Testing también se incluyen en el plan?
R. Sí. Si los objetivos seleccionados contienen pruebas de XCTest y Swift Testing, puedes ejecutarlas en el mismo plan. Conocer también Etiquetas y ejecución en paralelo de Swift Testing facilita dividir los planes.
P. ¿Y si quiero excluir solo algunas pruebas?
R. En la pestaña Tests puedes desmarcar objetivos, suites, funciones y casos parametrizados individuales. En Xcode 16 y versiones posteriores también puedes seleccionar el alcance con Include Tags y Exclude Tags de Swift Testing.
Como queda registrado solo en el archivo del plan, sin tocar el código, se ejecutará normalmente en los demás planes.
Un plan de pruebas no sirve para escribir pruebas nuevas. Sirve para decidir cuáles de las pruebas existentes ejecutar y bajo qué condiciones.
Si estabas separando las condiciones mediante duplicados del esquema, guarda primero el plan predeterminado y organízalo después en planes por propósito. En CI, especificar planes y configuraciones por trabajo aclara el alcance, pero la reducción real del tiempo depende del número de runners y de la estrategia de paralelización.
Empieza guardando el plan predeterminado en Product > Scheme > Edit Test Plan y creando un solo plan Smoke. Después será mucho más fácil añadir configuraciones.
Fuentes y criterios de verificación
- Improving code assessment by organizing tests into test plans (Apple) — Se verificaron el menú actual de creación de planes en Xcode, las unidades de selección de pruebas, la ejecución por configuración y la opción
xcodebuild. - Testing in Xcode, WWDC19 (Apple) — Se verificaron la introducción en Xcode 11, la estructura de archivos
.xctestplany el flujo de conversión de esquemas existentes. - Xcode 13 Release Notes (Apple) — Se verificaron los modos de repetición, el número entero positivo de repeticiones y la prioridad de las opciones de línea de comandos.
- Go further with Swift Testing, WWDC24 (Apple) — Se verificaron la ejecución paralela predeterminada y el orden aleatorio de Swift Testing.
- Diagnosing memory, thread, and crash issues early (Apple) — Se verificaron el propósito de los sanitizers y el coste de ejecución de Address Sanitizer.
La fecha de referencia es el 16 de agosto de 2026. Se priorizaron los nombres de menú y la sintaxis de línea de comandos actuales de la documentación oficial de Apple; no se afirmaron valores predeterminados de repetición ni cifras de reducción de tiempo en CI que no aparecen en la documentación.

