Una cosa molesta al ejecutar pruebas en Xcode 6.1 es que toda la aplicación tiene que ejecutarse e iniciar su guión gráfico y el controlador de vista raíz. En mi aplicación, esto ejecuta algunas llamadas al servidor que obtienen datos de la API. Sin embargo, no quiero que la aplicación haga esto al ejecutar sus pruebas.
Con las macros de preprocesador desaparecidas, ¿qué es lo mejor para que mi proyecto sepa que se lanzó ejecutando pruebas y no un lanzamiento normal? Los ejecuto normalmente con command+ Uy en un bot.
Pseudocódigo:
// Appdelegate.swift
if runningTests() {
return
} else {
// do ordinary api calls
}
Respuestas:
En lugar de verificar si las pruebas se están ejecutando para evitar efectos secundarios, puede ejecutar las pruebas sin la aplicación de host. Vaya a Configuración del proyecto -> seleccione el objetivo de prueba -> General -> Pruebas -> Aplicación de host -> seleccione 'Ninguno'. Solo recuerde incluir todos los archivos que necesita para ejecutar las pruebas, así como las bibliotecas que normalmente incluye el destino de la aplicación Host.
fuente
La respuesta de Elvind no es mala si quieres tener lo que solía llamarse "pruebas lógicas" puras. Si aún desea ejecutar su aplicación host contenedora pero ejecutar condicionalmente o no ejecutar código dependiendo de si se ejecutan pruebas, puede usar lo siguiente para detectar si se ha inyectado un paquete de prueba:
if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil { // Code only executes when tests are running }
Utilicé una marca de compilación condicional como se describe en esta respuesta para que el costo de tiempo de ejecución solo se incurre en las compilaciones de depuración:
#if DEBUG if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil { // Code only executes when tests are running } #endif
Editar Swift 3.0
if ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil { // Code only executes when tests are running }
fuente
XCTestConfigurationFilePath
clave de entorno en lugar deXCInjectBundle
.po ProcessInfo.processInfo.environment
no tiene claveXCTestConfigurationFilePath
. ¿Puedes compartir tu código? Esto se comprueba contra un objetivo de UITestNSClassFromString("XCTest")
método siguiente?Yo uso esto en la aplicación: didFinishLaunchingWithOptions:
// Return if this is a unit test if let _ = NSClassFromString("XCTest") { return true }
fuente
Otro, en mi opinión de forma más sencilla:
Edita su esquema para pasar un valor booleano como argumento de lanzamiento a su aplicación. Me gusta esto:
Todos los argumentos de lanzamiento se agregan automáticamente a su
NSUserDefaults
.Ahora puede obtener el BOOL como:
BOOL test = [[NSUserDefaults standardUserDefaults] boolForKey:@"isTest"];
fuente
"XCTestConfigurationFilePath"
oNSClassFromString("XCTest")
. Implementé esta solución en Swift con una función globalfunc isRunningTests() -> Bool { return UserDefaults.standard.bool(forKey: "isRunningTests") }
Creo que es completamente legítimo querer saber si se está ejecutando dentro de una prueba o no. Existen numerosas razones por las que esto puede resultar útil. Por ejemplo, al ejecutar pruebas, regreso antes de los métodos de ejecución de la aplicación / finalizará el lanzamiento en el Delegado de la aplicación, lo que hace que las pruebas comiencen más rápido para el código que no es pertinente para mi prueba unitaria. Sin embargo, no puedo hacer una prueba de pura "lógica", por una serie de otras razones.
Solía usar la excelente técnica descrita por @Michael McGuire anteriormente. Sin embargo, noté que dejó de funcionar para mí alrededor de Xcode 6.4 / iOS8.4.1 (quizás se rompió antes).
Es decir, ya no veo el XCInjectBundle cuando ejecuto una prueba dentro de un objetivo de prueba para un marco mío. Es decir, estoy corriendo dentro de un objetivo de prueba que prueba un marco.
Entonces, utilizando el enfoque que sugiere @Fogmeister, cada uno de mis esquemas de prueba ahora establece una variable de entorno que puedo verificar.
Luego, aquí hay un código que tengo en una clase llamada
APPSTargetConfiguration
que puede responderme esta simple pregunta.static NSNumber *__isRunningTests; + (BOOL)isRunningTests; { if (!__isRunningTests) { NSDictionary *environment = [[NSProcessInfo processInfo] environment]; NSString *isRunningTestsValue = environment[@"APPS_IS_RUNNING_TEST"]; __isRunningTests = @([isRunningTestsValue isEqualToString:@"YES"]); } return [__isRunningTests boolValue]; }
La única advertencia con este enfoque es que si ejecuta una prueba desde el esquema de su aplicación principal, como XCTest le permitirá hacer (es decir, sin seleccionar uno de sus esquemas de prueba), no obtendrá este conjunto de variables de entorno.
fuente
var isRunningTests: Bool { return ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil }
Uso
if isRunningTests { return "lena.bmp" } return "facebook_profile_photo.bmp"
fuente
Enfoque combinado de @Jessy y @Michael McGuire
(La respuesta aceptada no le ayudará mientras desarrolla un marco)
Así que aquí está el código:
#if DEBUG if (NSClassFromString(@"XCTest") == nil) { // Your code that shouldn't run under tests } #else // unconditional Release version #endif
fuente
He aquí una forma que he estado usando en Swift 4 / Xcode 9 para nuestras pruebas unitarias. Está basado en la respuesta de Jesse .
No es fácil evitar que se cargue el guión gráfico, pero si agrega esto al comienzo de didFinishedLaunching, dejará muy claro a sus desarrolladores lo que está sucediendo:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { #if DEBUG if let _ = NSClassFromString("XCTest") { // If we're running tests, don't launch the main storyboard as // it's confusing if that is running fetching content whilst the // tests are also doing so. let viewController = UIViewController() let label = UILabel() label.text = "Running tests..." label.frame = viewController.view.frame label.textAlignment = .center label.textColor = .white viewController.view.addSubview(label) self.window!.rootViewController = viewController return true } #endif
(¡Obviamente, no debería hacer nada como esto para las pruebas de IU en las que desea que la aplicación se inicie normalmente!)
fuente
Puede pasar argumentos en tiempo de ejecución a la aplicación según el esquema aquí ...
Pero me preguntaría si realmente es necesario o no.
fuente
Esta es la forma rápida de hacerlo.
extension Thread { var isRunningXCTest: Bool { for key in self.threadDictionary.allKeys { guard let keyAsString = key as? String else { continue } if keyAsString.split(separator: ".").contains("xctest") { return true } } return false } }
Y así es como lo usas:
if Thread.current.isRunningXCTest { // test code goes here } else { // other code goes here }
Aquí está el artículo completo:
https://medium.com/@theinkedengineer/check-if-app-is-running-unit-tests-the-swift-way-b51fbfd07989
fuente
return threadDictionary.allKeys.anyMatch { ($0 as? String)?.split(separator: ".").contains("xctest") == true }
Algunos de estos enfoques no funcionan con UITests y si básicamente está probando con el código de la aplicación en sí (en lugar de agregar un código específico en un objetivo de UITest).
Terminé configurando una variable de entorno en el método de configuración de la prueba:
XCUIApplication *testApp = [[XCUIApplication alloc] init]; // set launch environment variables NSDictionary *customEnv = [[NSMutableDictionary alloc] init]; [customEnv setValue:@"YES" forKey:@"APPS_IS_RUNNING_TEST"]; testApp.launchEnvironment = customEnv; [testApp launch];
Tenga en cuenta que esto es seguro para mis pruebas, ya que actualmente no uso ningún otro valor de launchEnvironment; si lo hace, por supuesto, querrá copiar primero los valores existentes.
Luego, en el código de mi aplicación, busco esta variable de entorno si / cuando quiero excluir alguna funcionalidad durante una prueba:
BOOL testing = false; ... if (! testing) { NSDictionary *environment = [[NSProcessInfo processInfo] environment]; NSString *isRunningTestsValue = environment[@"APPS_IS_RUNNING_TEST"]; testing = [isRunningTestsValue isEqualToString:@"YES"]; }
Nota: gracias por el comentario de RishiG que me dio esta idea; Simplemente amplié eso a un ejemplo.
fuente
El método que había estado usando dejó de funcionar en Xcode 12 beta 1. Después de probar todas las respuestas basadas en la compilación a esta pregunta, me inspiré en la respuesta de @ ODB. Aquí hay una versión Swift de una solución bastante simple que funciona tanto para dispositivos reales como para simuladores. También debería ser bastante "a prueba de versiones".
Insertar en configuración de prueba:
let app = XCUIApplication() app.launchEnvironment.updateValue("YES", forKey: "UITesting") app.launch()
Insertar en la aplicación:
let isTesting: Bool = (ProcessInfo.processInfo.environment["UITesting"] == "YES")
Para usarlo:
if isTesting { // Only if testing } else { // Only if not testing }
fuente
Trabajó para mi:
C objetivo
[[NSProcessInfo processInfo].environment[@"DYLD_INSERT_LIBRARIES"] containsString:@"libXCTTargetBootstrapInject"]
Rápido:
ProcessInfo.processInfo.environment["DYLD_INSERT_LIBRARIES"]?.contains("libXCTTargetBootstrapInject") ?? false
fuente
Primero agregue la variable para probar:
y usa eso en tu código:
if ProcessInfo.processInfo.environment["IS_UNIT_TESTING"] == "1" { // Code only executes when tests are running }
fuente
Aparentemente, en Xcode12 necesitamos buscar en la clave de entorno en
XCTestBundlePath
lugar deXCTestConfigurationFilePath
si está utilizando el nuevoXCTestPlan
fuente