En ingeniería de software , la prueba de interfaz gráfica de usuario es el proceso de probar la interfaz gráfica de usuario (GUI) de un producto para garantizar que cumple con sus especificaciones. Esto normalmente se hace mediante el uso de una variedad de casos de prueba .
Para generar un conjunto de casos de prueba , los diseñadores de pruebas intentan cubrir toda la funcionalidad del sistema y ejercitar por completo la propia GUI . La dificultad para lograr esta tarea es doble: hay que lidiar con el tamaño del dominio y las secuencias. Además, el evaluador se enfrenta a más dificultades cuando tiene que realizar pruebas de regresión .
A diferencia de un sistema CLI (interfaz de línea de comandos), una GUI puede tener operaciones adicionales que se deben probar. Un programa relativamente pequeño como Microsoft WordPad tiene 325 operaciones de GUI posibles. [1] En un programa grande, la cantidad de operaciones puede ser fácilmente un orden de magnitud mayor.
El segundo problema es el de la secuenciación. Algunas funciones del sistema solo se pueden llevar a cabo con una secuencia de eventos de la interfaz gráfica de usuario. Por ejemplo, para abrir un archivo, el usuario puede tener que hacer clic en el menú Archivo, seleccionar la operación Abrir, utilizar un cuadro de diálogo para especificar el nombre del archivo y centrar la aplicación en la ventana recién abierta. Aumentar el número de operaciones posibles aumenta exponencialmente el problema de la secuenciación. Esto puede convertirse en un problema grave cuando el evaluador crea casos de prueba manualmente.
Las pruebas de regresión también suelen ser un desafío con las GUI. Una GUI puede cambiar significativamente, aunque la aplicación subyacente no lo haga. Una prueba diseñada para seguir una determinada ruta a través de la GUI puede fallar debido a que un botón, un elemento de menú o un cuadro de diálogo pueden haber cambiado de ubicación o apariencia.
Estos problemas han llevado el dominio de los problemas de prueba de GUI hacia la automatización. Se han propuesto muchas técnicas diferentes para generar automáticamente conjuntos de pruebas que sean completos y que simulen el comportamiento del usuario.
La mayoría de las técnicas de prueba intentan basarse en las que se utilizaban anteriormente para probar programas CLI, pero pueden tener problemas de escalabilidad cuando se aplican a las GUI. Por ejemplo, el modelado basado en máquinas de estados finitos [2] [3] , donde un sistema se modela como una máquina de estados finitos y se utiliza un programa para generar casos de prueba que ejercitan todos los estados, puede funcionar bien en un sistema que tiene una cantidad limitada de estados, pero puede volverse demasiado complejo y difícil de manejar para una GUI (consulte también pruebas basadas en modelos ).
Un nuevo enfoque para la generación de conjuntos de pruebas, adaptado de una técnica CLI [4], implica el uso de un sistema de planificación. [5] La planificación es una técnica bien estudiada del dominio de la inteligencia artificial (IA) que intenta resolver problemas que involucran cuatro parámetros:
Los sistemas de planificación determinan una ruta desde el estado inicial hasta el estado objetivo mediante el uso de operadores. Como ejemplo simple de un problema de planificación, dadas dos palabras y una sola operación que reemplaza una sola letra de una palabra por otra, el objetivo podría ser cambiar una palabra por otra.
En [1] los autores utilizaron el planificador IPP [6] para demostrar esta técnica. Primero se analiza la interfaz de usuario del sistema para determinar las posibles operaciones, que se convierten en los operadores utilizados en el problema de planificación. A continuación, se determina un estado inicial del sistema y se especifica un estado objetivo que, según el evaluador, permitiría poner a prueba el sistema. El sistema de planificación determina una ruta desde el estado inicial hasta el estado objetivo, que se convierte en el plan de prueba.
El uso de un planificador para generar los casos de prueba tiene algunas ventajas específicas con respecto a la generación manual. Un sistema de planificación, por su propia naturaleza, genera soluciones a los problemas de planificación de una manera que resulta muy beneficiosa para el evaluador:
Al crear manualmente un conjunto de pruebas, el evaluador se centra más en cómo probar una función (es decir, la ruta específica a través de la GUI). Al utilizar un sistema de planificación, se tiene en cuenta la ruta y el evaluador puede centrarse en qué función probar. Un beneficio adicional de esto es que un sistema de planificación no tiene ninguna restricción al generar la ruta y, a menudo, puede encontrar una ruta que el evaluador nunca anticipó. Este problema es muy importante de combatir. [7]
Otro método para generar casos de prueba de GUI simula un usuario novato. Un usuario experto de un sistema tiende a seguir un camino directo y predecible a través de una GUI, mientras que un usuario novato seguiría un camino más aleatorio. Es probable que un usuario novato explore más estados posibles de la GUI que un experto.
La dificultad radica en generar conjuntos de pruebas que simulen el uso del sistema por parte de un "principiante". Se ha propuesto el uso de algoritmos genéticos para resolver este problema. [7] Los caminos que siguen los novatos a través del sistema no son aleatorios. En primer lugar, un usuario novato aprenderá con el tiempo y, por lo general, no cometerá los mismos errores repetidamente y, en segundo lugar, un usuario novato sigue un plan y probablemente tenga algún conocimiento del dominio o del sistema.
Los algoritmos genéticos funcionan de la siguiente manera: se crea un conjunto de "genes" aleatoriamente y luego se los somete a una tarea. Los genes que mejor completan la tarea se conservan y los que no lo hacen se descartan. El proceso se repite nuevamente con los genes supervivientes que se replican y el resto del conjunto se completa con más genes aleatorios. Al final, un gen (o un pequeño conjunto de genes si hay un umbral establecido) será el único gen del conjunto y, naturalmente, el que mejor se adapta al problema dado.
En el caso de las pruebas de interfaz gráfica de usuario, el método funciona de la siguiente manera. Cada gen es esencialmente una lista de valores enteros aleatorios de una longitud fija. Cada uno de estos genes representa una ruta a través de la interfaz gráfica de usuario. Por ejemplo, para un árbol de widgets determinado, el primer valor del gen (cada valor se denomina alelo) seleccionaría el widget sobre el que se operaría, y los siguientes alelos completarían la entrada del widget en función de la cantidad de entradas posibles (por ejemplo, un cuadro de lista desplegable tendría una entrada... el valor de la lista seleccionada). El éxito de los genes se califica según un criterio que recompensa el mejor comportamiento "principiante".
En [7] se describe un sistema para realizar estas pruebas en el sistema de ventanas X, pero que es extensible a cualquier sistema de ventanas. El sistema X Window proporciona una funcionalidad (a través de XServer y el protocolo de los editores) para enviar dinámicamente una entrada de GUI y obtener una salida de GUI del programa sin usar directamente la GUI. Por ejemplo, se puede llamar a XSendEvent() para simular un clic en un menú desplegable, etc. Este sistema permite a los investigadores automatizar la creación y prueba de genes, de modo que para cualquier aplicación dada bajo prueba, se puede crear un conjunto de casos de prueba de usuario novato.
Al principio, las estrategias se migraron y adaptaron desde las estrategias de prueba CLI.
Un método popular utilizado en el entorno CLI es la captura/reproducción. La captura/reproducción es un sistema en el que la pantalla del sistema se "captura" como un gráfico de mapa de bits en varios momentos durante la prueba del sistema. Esta captura le permite al evaluador "reproducir" el proceso de prueba y comparar las pantallas en la fase de salida de la prueba con las pantallas esperadas. Esta validación podría automatizarse ya que las pantallas serían idénticas si el caso se superaba y diferentes si el caso fallaba.
El uso de captura/reproducción funcionó bastante bien en el mundo de la interfaz de línea de comandos, pero existen problemas significativos cuando uno intenta implementarlo en un sistema basado en una interfaz gráfica de usuario. [8] El problema más obvio que uno encuentra es que la pantalla en un sistema con interfaz gráfica de usuario puede verse diferente mientras que el estado del sistema subyacente es el mismo, lo que hace que la validación automática sea extremadamente difícil. Esto se debe a que una interfaz gráfica de usuario permite que los objetos gráficos varíen en apariencia y ubicación en la pantalla. Las fuentes pueden ser diferentes, los colores o tamaños de las ventanas pueden variar, pero la salida del sistema es básicamente la misma. Esto sería obvio para un usuario, pero no obvio para un sistema de validación automática.
Para combatir este y otros problemas, los evaluadores han ido "bajo el capó" y han recopilado datos de interacción de la GUI del sistema de ventanas subyacente. [9] Al capturar los "eventos" de la ventana en registros, las interacciones con el sistema ahora están en un formato que está desacoplado de la apariencia de la GUI. Ahora, solo se capturan los flujos de eventos. Hay algún filtrado de los flujos de eventos necesario ya que los flujos de eventos suelen ser muy detallados y la mayoría de los eventos no son directamente relevantes para el problema. Este enfoque se puede hacer más fácil utilizando una arquitectura MVC , por ejemplo, y haciendo que la vista (es decir, la GUI aquí) sea lo más simple posible mientras que el modelo y el controlador contienen toda la lógica. Otro enfoque es utilizar la tecnología de asistencia incorporada del software , utilizar una interfaz HTML o una arquitectura de tres niveles que también permita separar mejor la interfaz de usuario del resto de la aplicación.
Otra forma de ejecutar pruebas en una GUI es crear un controlador en la GUI para que se puedan enviar comandos o eventos al software desde otro programa. [7] Este método de enviar y recibir eventos directamente a un sistema es muy conveniente durante las pruebas, ya que las pruebas de entrada y salida se pueden automatizar por completo y se eliminan los errores del usuario.