xcode5 - ¿Cómo migro de SenTestingKit/OCUnit a XCTest?
(2)
Editar -> Refactorizar -> Convertir a XCTest
Las pruebas de OCUnit seguirán funcionando, pero también podría migrar. Los cambios terminan siendo bastante mínimos.
Estoy en el proceso de migrar mi proyecto de Xcode 4.6.3 a Xcode 5.0.2. Las pruebas unitarias del proyecto se desarrollaron con SenTestingKit / OCUnit. Ahora cuando estoy ejecutando las pruebas en Xcode 5, recibo un error del script RunUnitTests
diciéndome que
RunUnitTests está obsoleto.
Posiblemente relacionado está esta nota en las notas de la versión de Xcode 5:
SenTestingKit y OCUnit están en desuso. Usa el migrador para moverte a XCTest.
Lamentablemente, no he podido obtener más información sobre este misterioso "migrador". Posiblemente mi google-fu carece [de nuevo], así que mi pregunta principal es: ¿cómo migro las pruebas unitarias desde SenTestingKit / OCUnit al nuevo XCTest (con o sin el "migrador")?
Una pregunta secundaria, en caso de que la migración sea una tarea complicada: ¿es posible hacer que Xcode 5 ejecute pruebas unitarias que todavía se basan en SenTestingKit / OCUnit? Después de todo esto son simplemente obsoletos, por lo que todavía deberían estar disponibles y funcionales.
Gracias a la respuesta de Shaggy Frog sabemos que el misterioso "migrador" mencionado en las notas de la versión de Xcode es un asistente lanzado al seleccionar "Editar> Refactorizar> Convertir a XCTest". Voy a escribir sobre mi experiencia con este asistente en dos partes. La primera parte es una respuesta incompleta a la pregunta principal, la segunda parte responde a la pregunta secundaria.
Parte 1: Cómo migrar de OCUnit a XCTest
Lo primero que debe tener en cuenta es que para que el asistente funcione, debe seleccionar un objetivo de prueba unitario . Si tiene seleccionado el objetivo principal, el asistente simplemente no enumera ningún objetivo para convertir.
Una vez que me enteré de esto, pude pasar por el asistente, pero en mi caso el resultado final fue un fracaso espectacular. El asistente afirmó que no era necesario realizar cambios en la fuente y que solo era necesario actualizar la configuración para migrar a XCTest. Al final, el asistente ni siquiera logró hacerlo correctamente: eliminó la referencia al marco SenTestingKit, pero no incluyó una referencia al marco XCTest.
De todos modos, lo que sigue es una lista de los cambios que tuve que hacer manualmente porque el asistente no pudo hacerlos por mí. Si el asistente funciona mejor para usted, es posible que no necesite hacer todas estas cosas.
- Elimine la fase de compilación "Ejecutar secuencia de comandos" del objetivo de prueba de unidad
- Cambie la clase base de todas las clases de casos de prueba de
SenTestCase
aXCTestCase
- Cambie el encabezado importado de
<SenTestingKit/SenTestingKit.h>
a<XCTest/XCTest.h>
- En la configuración de compilación del objetivo de prueba, cambie la extensión de
octest
deoctest
axctest
. - Cambie el nombre de todas las macros assert de
ST*
aXCT*
(pSTAssertTrue
Ej.STAssertTrue
convierte enXCTAssertTrue
) - Excepción a lo anterior:
STAssertEquals
necesita ser renombrado aXCTAssertEqual
(observe la "s" que falta al final). Sabrá que se ha olvidado de esto si obtiene esta advertencia del compilador:warning: implicit declaration of function ''XCTAssertEquals'' is invalid in C99
- Las nuevas macros XCTest assert no permiten que se pase
nil
como descripción de la falla. Por ejemplo,XCTAssertNotNil(anObject, nil)
no es posible y se debe cambiar aXCTAssertNotNil(anObject)
. Sabrá que tiene este problema cuando obtenga este error de compilación:error: called object type ''NSString *'' is not a function or function pointer
. - Si necesita pasar una descripción de falla, las nuevas macros de XCTest assert requieren una expresión constante para el especificador de formato, tal como lo
NSString
método de clasestringWithFormat:
Sabrá que tiene este problema cuando obtenga este error de compilación:error: expected '')''
. Algunos ejemplos:
NSString* formatSpecifier = @"%@";
NSString* failureDescription = @"foo";
// These are OK
XCTAssertNotNil(anObject, @"foo")
XCTAssertNotNil(anObject, @"%@", failureDescription)
// These are not OK
XCTAssertNotNil(anObject, failureDescription);
XCTAssertNotNil(anObject, formatSpecifier, failureDescription);
Por último, pero no por ello menos importante, como ya se mencionó anteriormente, la referencia al marco XCTest debe agregarse al objetivo de la prueba unitaria. Sabrá que lo ha olvidado si obtiene errores del enlazador, como los Undefined symbols for architecture i386: "_OBJC_CLASS_$_XCTestCase", referenced from: foo
.
Actualización de Xcode 6 : ya no se necesita vincular con XCTest en Xcode 6 (de hecho, XCTest ya no figura como un marco disponible). En su lugar, establezca la configuración de compilación CLANG_ENABLE_MODULES en YES (expuesta en la interfaz de usuario como "Habilitar módulos (C y Objective-C)"). Esto hará que clang
se vincule automáticamente contra XCTest cuando vea una declaración #import <XCTest/XCTest.h>
. Los detalles están disponibles en la sección "Módulos" de la documentación de clang .
Parte 2: Cómo ejecutar pruebas OCUnit en Xcode 5
En este punto, recibí un error del enlazador que me hizo darme cuenta de que mi misión de migrar a XCTest había fallado. El motivo: XCTest no forma parte de SDK 6.1, pero aún estoy desarrollando mi proyecto con el SDK base iOS 6.1 ( esta respuesta SO explica cómo integrar SDK 6.1 en Xcode 5).
Como no puedo continuar con la migración, mi solución por el momento es mantener mis pruebas de unidad basadas en SenTestingKit / OCUnit, hasta que encuentre el momento de actualizar mi aplicación a iOS 7. Esto es lo que tenía que hacer para poder para ejecutar las pruebas unitarias:
- Elimine la fase de compilación "Ejecutar secuencia de comandos" del objetivo de prueba de la unidad. Esto es todo lo que se requiere para permitir que Xcode ejecute pruebas unitarias a través de la acción "Prueba" ( ⌘ + U ) mientras se selecciona el objetivo de prueba de la unidad .
- Esto no es ideal, sin embargo, porque no quiero cambiar los objetivos solo para ejecutar pruebas unitarias. En cambio, quiero ejecutar pruebas unitarias mientras el objetivo principal está seleccionado . El segundo paso, por lo tanto, es modificar el esquema Xcode del objetivo principal para que cuando ejecute la acción "Prueba", las pruebas del objetivo de prueba unitario se ejecuten en su lugar.
La solución final no es tan buena como en Xcode 4.x donde las pruebas unitarias se ejecutaron automáticamente cada vez que ejecuté la acción "Ejecutar" o "Construir" del objetivo principal. Desafortunadamente, parece que no puedo hacer que esto funcione sin una fase de compilación "Ejecutar script".