unit test ios swift xctest xcode-ui-testing

ios - unit test swift 4



Pruebas de la interfaz de usuario de iOS en una vista aislada (3)

¡Absolutamente!

Lo que necesita es un entorno de aplicación limpio en el que pueda ejecutar sus pruebas, una pizarra en blanco.

Todas las aplicaciones tienen un delegado de aplicación que configura el estado inicial de la aplicación y proporciona un controlador de vista raíz en el inicio. Para los propósitos de la prueba, usted no quiere que eso suceda, necesita poder realizar la prueba de manera aislada, sin que todas esas cosas sucedan. Lo ideal es que usted quiera poder tener la prueba más baja de la pantalla y solo esa pantalla cargada, y no ocurran otros cambios de estado.

Para hacerlo, puede crear un objeto solo para la prueba que implementa UIApplicationDelegate . Puede decirle a la aplicación que se ejecute en "modo de prueba" y usar el delegado de aplicación específico de la prueba usando un argumento de lanzamiento.

Objective-C: main.m:

int main(int argc, char * argv[]) { NSString * const kUITestingLaunchArgument = @"org.quellish.UITestingEnabled"; @autoreleasepool { if ([[NSUserDefaults standardUserDefaults] valueForKey:kUITestingLaunchArgument] != nil){ return UIApplicationMain(argc, argv, nil, NSStringFromClass([TestingApplicationDelegate class])); } else { return UIApplicationMain(argc, argv, nil, NSStringFromClass([ProductionApplicationDelegate class])); } } }

Swift: main.swift:

let kUITestingLaunchArgument = "org.quellish.UITestingEnabled" if (NSUserDefaults.standardUserDefaults().valueForKey(kUITestingLaunchArgument) != nil){ UIApplicationMain(Process.argc, Process.unsafeArgv, NSStringFromClass(UIApplication), NSStringFromClass(TestingApplicationDelegate)) } else { UIApplicationMain(Process.argc, Process.unsafeArgv, NSStringFromClass(UIApplication), NSStringFromClass(AppDelegate)) }

Deberá eliminar cualquier anotación @UIApplicationMain de sus clases de Swift.

Para "pruebas de aplicación", asegúrese de establecer la acción "Prueba" del esquema en Xcode para proporcionar el argumento de lanzamiento:

Para las pruebas de interfaz de usuario, puede establecer los argumentos de lanzamiento como parte de la prueba:

C objetivo:

XCUIApplication *app = [[XCUIApplication alloc] init]; [app setLaunchArguments:@[@"org.quellish.UITestingEnabled"] ]; [app launch];

Rápido:

let app = XCUIApplication() app.launchArguments = [ "org.quellish.UITestingEnabled" ] app.launch()

Esto permite que las pruebas usen un delegado de aplicación específicamente para las pruebas. Esto le otorga un gran control: ahora tiene una pizarra en blanco con la que trabajar para las pruebas. El delegado de la aplicación de prueba puede cargar un guión gráfico específico o colocar un UIViewController vacío. Como parte de sus pruebas de interfaz de usuario, puede crear una instancia del controlador de vista que se está probando y establecerlo como el controlador de vista raíz del keyWindow o presentarlo de manera modal. Una vez que se ha agregado o presentado, sus pruebas pueden ejecutarse, y cuando finalice, elimínelas o descártelas.

Estoy tratando de incorporar las pruebas de UI en mi proyecto de iOS, pero una cosa que sigue demorándome es el hecho de que parece que todas las pruebas que escribes deben comenzar desde el principio de la aplicación y seguir su camino. Por ejemplo, si quiero probar una vista que está detrás de una pantalla de inicio de sesión, mis pruebas deben ejecutarse primero en la pantalla de inicio de sesión, ingresar un nombre de usuario / contraseña, hacer clic en iniciar sesión y luego ir a la vista que deseo probar. Idealmente, las pruebas para la vista de inicio de sesión y la siguiente serían completamente aisladas. ¿Hay alguna forma de hacerlo o me estoy perdiendo completamente la filosofía detrás de las pruebas de UI?


Desafortunadamente, con UI Testing, el escenario que describe no es posible.

Un enfoque que tomo para combatir esto es agrupar mis pruebas en "flujos" de características. Por ejemplo, digamos que quiero probar la Característica A, la Característica B y la Característica C. Necesito iniciar sesión para que los tres funcionen.

Para cada prueba no lanzo la aplicación, inicio sesión y finalmente ejecuto la prueba real. En cambio, lance la aplicación e inicie sesión una vez. Luego testFeatureA() mi prueba en tres métodos de ayuda privada, testFeatureA() , testFeatureB() y testFeatureC() .

Al crear un solo flujo, el conjunto de pruebas tardará mucho más tiempo en ejecutarse. El gran inconveniente es que si la Característica A falla, la Característica B nunca será probada. Este enfoque solo debe usarse si le importa si todas sus pruebas pasan o no.

Puntos de bonificación por usar un ayudante de XCTest con los parámetros __LINE__ y __FILE__ predeterminados. Luego puede pasarlas a sus llamadas XCTFail() para mostrar la línea de falla en testFeatureA() .


Si no te importa la carga de la interfaz de usuario original, simplemente salta a la interfaz de usuario de destino con:

override func setUp() { super.setUp() continueAfterFailure = false XCUIApplication().launch() let storyboard = UIStoryboard(name: "MainStoryboard", bundle: NSBundle.mainBundle()) let controller = storyboard.instantiateViewControllerWithIdentifier("LanguageSelectController") UIApplication.sharedApplication().keyWindow?.rootViewController = controller }

Si no desea que se cargue la interfaz de usuario original debajo, también pase esto desde su prueba:

app.launchArguments.append("skipEntryViewController")

y luego en didFinishLaunchingWithOptions , puedes verificar:

if NSProcessInfo.processInfo().arguments.contains("skipEntryViewController") { // then do NOT call makeKeyAndVisible }