¿Cómo escribir pruebas de unidad mantenibles, no frágiles, para una GUI?

16

Intenté escribir pruebas unitarias de interfaz de usuario para mis aplicaciones GUI y me enfrento al problema de que, aunque funcionan bien cuando las escribo inicialmente, resultan quebradizas y se rompen cada vez que cambia el diseño (es decir, con bastante frecuencia). Estoy luchando por encontrar un conjunto de pautas que me lleven a tener pruebas unitarias mantenibles para la GUI.

Por ahora, una cosa que descubrí es que las pruebas que dicen "este componente debería mostrar sus datos de entrada en algún lugar" son buenas (y eso es muy fácil con HTML). Las pruebas que verifican un estado específico de una parte específica del componente suelen ser frágiles. Las pruebas que van como clic-clic-clic-esperar, que intentan seguir el comportamiento del usuario y la lógica empresarial subyacente (que es la parte más importante) generalmente resultan frágiles. ¿Cómo escribo buenas pruebas?


Para ser más precisos, me gustaría conocer algunos patrones sobre qué podría probar en mi interfaz de usuario, no exactamente cómo hacerlo. Las convenciones de nomenclatura y los identificadores fijos son buenos, pero no resuelven el problema central, es decir, que las GUI cambian mucho. Me gustaría probar los comportamientos que es poco probable que cambien. ¿Cómo encontrar lo correcto para probar?

mik01aj
fuente
1
@MichaelDurrant: Editó la pregunta sobre las pruebas de IU en general, mientras que originalmente le pregunté sobre las pruebas unitarias. Encuentro las pruebas de integración más difíciles de mantener y prefiero las pruebas unitarias sobre ellas, siempre que sea posible.
mik01aj
2
Sin embargo, creo que esto es parte del problema, fundamentalmente no se puede probar ninguna interfaz mediante pruebas unitarias solas, ya que su razón de ser es interactuar con algo. Las GUI no son diferentes a este respecto.
jk.
m01, puedes volver a cambiarlo. Creo que las pruebas de IU suelen ser pruebas integradas. Las pruebas tienden a depender de los datos de semillas y accesorios presentes y la interfaz de usuario que trabaja con ellos. Si tiene pruebas de IU verdaderas que no se basan en ningún otro dato que sea excelente. Sin embargo, he encontrado que esto es relativamente raro.
Michael Durrant
2
relacionado pero no un duplicado ya que se trata de pruebas de interfaz
gráfica de usuario

Respuestas:

3

Un problema común con las pruebas de GUI ... La razón principal por la que estas pruebas se consideran frágiles es porque no pueden sobrevivir a un cambio en la GUI que no es un cambio en los requisitos . Debe esforzarse por estructurar su código de prueba de tal manera que un cambio en la GUI esté aislado en un solo lugar en las pruebas.

Como ejemplo, considere una prueba redactada como:

Cuando el usuario ingresa '999' en el campo de número de teléfono y hace clic en el botón Guardar, la ventana emergente del mensaje de error debe decir 'número de teléfono no válido'.

Mucho espacio aquí para que esta prueba se rompa cuando reelaboras la interfaz, incluso si el requisito para la validación permanece.

Ahora, pongamos esto en una pequeña redacción alternativa:

Cuando el usuario ingresa '999' como número de teléfono y guarda la página de perfil, debe mostrar un error que dice 'número de teléfono no válido'

La prueba es la misma, los requisitos son los mismos, pero este tipo de prueba sobrevivirá a un cambio de imagen para su interfaz de usuario. Necesitará cambiar el código, obviamente, pero el código estará aislado. Incluso si tiene diez o veinte pruebas de este tipo para su página de perfil y mueve su lógica de validación de mensaje de error de alertas de JavaScript a jquery-popups, por ejemplo, solo necesita cambiar la parte de prueba única que verifica los mensajes de error.

JDT
fuente
4

Este es un problema común. Me gustaría prestar atención a:

  • Cómo nombras elementos

    Use css id o class para identificar elementos. Favorezca el uso de la ID de CSS cuando el objeto es único. Considere el marco que está utilizando, por ejemplo, con Ruby on Rails, el nameatributo se asigna automáticamente y puede (no intuitivamente) ser mejor que usar el id o la clase css

  • Cómo identificas los elementos.

    Evite identificadores posicionales como table/tr/td/tda favor de formas como td[id="main_vehicle"o td[class='alternates']. Considere usar atributos de datos cuando sea apropiado. Incluso mejor trate de evitar etiquetas de diseño como <td>por ejemplo, para lo anterior, puede agregar un intervalo y usarlo, por ejemplo, <span id="main_vehicle">o un selector de comodín, como *[id="main_vehicle"]donde *ahora podría ser un div, span, td, etc.

  • Usar atributos de datos específicos de prueba que solo se usan para qa y pruebas.

  • Evite la calificación innecesaria para los elementos. Puede encontrarse usando lo siguiente:

    body.main div#vehicles > form#vehicle input#primary_vehicle_name

    Sin embargo, esto requiere que el campo de entrada permanezca en un formulario con una identificación exacta del vehículo y en una página con un cuerpo que tenga una clase de main y un div con una identificación de vehículos que tenga un elemento secundario inmediato de un formulario con una identificación de vehículo. Cualquier cambio en cualquiera de esa estructura y la prueba se rompe. En este caso, puede encontrar que

    input#primary_vehicle_name

    es suficiente para identificar de forma única el elemento.

  • Evite las pruebas que hacen referencia a texto visible. El texto en la página que se muestra al usuario generalmente cambia con el tiempo a medida que el sitio se mantiene y actualiza, así que use identificadores como css id y css class o atributos de datos. Elementos como form, inputy selectutilizados en formularios también son buenas partes de elementos de identificación, generalmente en combinación con id o clase, por ejemplo, li.vehicleo input#first-vehicle También puede agregar sus propios identificadores, por ejemplo <div data-vehicle='dodge'>. De esta forma, puede evitar el uso de las ID o clases de elementos, que los desarrolladores y diseñadores pueden cambiar. De hecho, con el tiempo descubrí que es mejor trabajar con desarrolladores y diseñadores y llegar a un acuerdo sobre los nombres y los ámbitos. Es difícil.

  • Cómo se mantienen los datos fijos.

    Similar a la identificación de elementos reales, trate de evitar valores de identificación del selector codificado en línea a favor de los objetos de la página: pequeños fragmentos de texto que se mantienen en variables o métodos y, por lo tanto, pueden reutilizarse y también mantenerse centralmente. Ejemplos de variables de JavaScript que siguen este patrón para valores codificados:

    storedVars["eqv_auto_year"] = "2015";
    storedVars["eqv_auto_make_1"] = "ALFA ROMEO";
    storedVars["eqv_auto_make_2"] = "HONDA";`  
    

    Más sobre objetos de página en selenium wiki y selenium docs

  • Comunicación con desarrolladores.

    Independientemente del enfoque técnico en términos de "los desarrolladores realizan cambios y rompen la automatización del control de calidad", es un problema de flujo de trabajo. Debe asegurarse de que: todos son uno mismo equipo; el desarrollador ejecuta las mismas pruebas integradas; los estándares son acordados y seguidos por ambos grupos; la definición de hecho incluye ejecutar y posiblemente actualizar las pruebas de IU; los desarrolladores y el probador se emparejan en los planes de prueba y ambos asisten a la preparación del boleto (si está haciendo Agile) y hablan sobre las pruebas de IU como parte de la preparación. Debe asegurarse de que cualquier enfoque y estrategia que utilice para nombrar esté coordinado con los desarrolladores de aplicaciones. Si no llega a la misma página, le gustará el choque sobre la denominación de objetos. Algunos ejemplos de métodos de objetos de página que creé recientemente para un proyecto ruby:

    def css_email_opt_in_true
      'auto_policy[email_opt_in][value=1]'
    end 
    
    def css_phone_opt_in
      '*[name="auto_policy[phone_opt_in]"]'
    end 
    
    def css_phone_opt_in_true
      'input[name=phone_opt_in][value=true]'
    end 
    
    def css_credit_rating
      'auto_policy[credit_rating]'
    end
    

    Aquí están los mismos objetos de página que las variables de JavaScript:

    storedVars["css_email_opt_in"] = "css=*[name='auto_policy[email_opt_in]']";
    storedVars["css_phone_opt_in"]="css=*[name='auto_policy[phone_opt_in]']";
    storedVars["css_phone_opt_in_true"]="css=input[name='phone_opt_in'][value=true]";
    storedVars["css_credit_rating"]="css=select[name='auto_policy[credit_rating]']";
    
Michael Durrant
fuente
2
Estas son sugerencias útiles, y en realidad ya sigo la mayoría de ellas. Mi problema fue que escribí pruebas para algún botón, y luego este botón se elimina mientras la misma acción se maneja desde otro lugar. O el botón permanece allí, pero la etiqueta cambia y el botón ejecuta también alguna acción adicional.
mik01aj
1
Ahh, eso es bueno saberlo. Sí, por esas cosas me enfocaría en el flujo de trabajo y la interacción organizacional qa-desarrollador
Michael Durrant
1
También estoy publicando información para los demás que encuentran su pregunta y pueden no tener todas las piezas en su lugar que usted tiene o puede que ni siquiera sepa sobre ellas.
Michael Durrant
1
He ampliado mi último punto basado en sus comentarios.
Michael Durrant
1
Y he actualizado las partes sobre nombrar e identificar elementos y sobre no usar texto visible.
Michael Durrant
3

La razón por la cual las personas desarrollaron cosas como MVC, MVP y presentador primero, y patrones de diseño similares, fue para separar la lógica empresarial de la interfaz de usuario.

Obviamente, la parte de vista solo se puede probar iniciando el programa y verificando lo que muestra; en otras palabras, solo se puede probar en las pruebas de aceptación.

Por otro lado, la prueba de la lógica empresarial se puede hacer en pruebas unitarias. Y esa es la respuesta a tu pregunta. Pruebe todo en el modelo, y si puede y quiere, también puede probar el código del controlador.


Las GUI cambian mucho

Eso solo puede suceder cuando tienes requisitos cambiantes. Cuando un requisito cambia, no hay forma de evitarlo, excepto modificar el código. Si logra crear un buen diseño y arquitectura, el cambio no se propagará en muchos lugares.

BЈовић
fuente
2
Creo que es un buen punto. Tal vez solo estoy haciendo MVC mal :) Por cierto, creo que los requisitos cambiantes son inevitables cuando desarrollas una plataforma web para muchos usuarios. No sabe cómo se comportarán sus usuarios hasta que comiencen a usar la GUI.
mik01aj
2

Las pruebas de interacción GUI no deben ser más o menos frágiles que cualquier otro tipo de pruebas. Es decir; Si su aplicación está cambiando de alguna manera, las pruebas deben actualizarse para reflejarlo.

Como una comparación:

Prueba de unidad

Original : validateEmail()debe lanzar unInvalidData excepción. Que está cubierto correctamente en su prueba de unidad.

Cambio : validateEmail()debería lanzar una InvalidEmailexcepción. Ahora su prueba es incorrecta, la actualiza y todo vuelve a estar verde.

Prueba de GUI

Original : al ingresar un correo electrónico no válido, aparecerá un cuadro de error emergente que contiene "Datos ingresados ​​no válidos". Detectado correctamente por sus pruebas.

Cambio : ingresar un correo electrónico no válido dará como resultado un error en línea que contiene "Correo electrónico no válido ingresado". Ahora su prueba es incorrecta, la actualiza y todo vuelve a estar verde.


Recuerde que está probando entradas y salidas, un comportamiento bien definido. Independientemente de si es una prueba de GUI o una prueba de Unidad o una prueba de Integración, etc.

Jess Telford
fuente