Pruebas y calidad de código

Guía completa de planes de pruebas de Xcode

Los planes de pruebas gestionan en archivos .xctestplan las pruebas que se ejecutarán y sus condiciones. Aquí se explica por qué la configuración del esquema no basta, además de la selección de objetivos, las configuraciones, la cobertura, el orden, la repetición y la integración con xcodebuild.

11 min de lectura
Imagen de portada de Guía completa de planes de pruebas de Xcode

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.

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.

Diagrama jerárquico de esquemas y configuraciones de planes de pruebas en Xcode, con ramas Smoke, Regression y Nightly
Puedes ampliar las combinaciones de ejecución sin crear pruebas nuevas

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.

  1. Abre el plan predeterminado en Product > Scheme > Edit Test Plan y guárdalo
  2. En la pestaña Tests, elige los objetivos de ejecución por etiqueta, suite o función
  3. 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.

Ilustración de una canalización de ejecución de pruebas con el texto CI PIPELINE, dividida en pull request, merge y compilación nocturna
Ligero en los PR y pesado por la noche: dividir los planes lo hace posible

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

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.