Pruebas de UI automatizadas y mantenibles

Hace unos años, era muy escéptico acerca de las pruebas automatizadas de UI y este escepticismo nació de algunos intentos fallidos. Escribiría algunas pruebas automatizadas de UI para aplicaciones de escritorio o web y unas semanas más tarde las eliminaría de la base de código porque el costo de mantenerlas era demasiado alto. Así que pensé que la prueba de la interfaz de usuario era difícil y que, si bien proporcionaba muchos beneficios, era mejor mantenerla al mínimo y solo probar los flujos de trabajo más complejos en un sistema a través de la prueba de la interfaz de usuario y dejar el resto a pruebas de unidad. Recuerdo que le conté a mi equipo sobre la pirámide de pruebas de Mike Cohn, y que en un sistema típico, más del 70% de las pruebas deberían ser pruebas unitarias, alrededor del 5% de pruebas de UI y el resto de pruebas de integración.

Así que pensé que las pruebas de UI eran difíciles y que, si bien ofrecía muchos beneficios, era mejor mantenerlo al mínimo ...

¡Estaba equivocado! Claro, las pruebas de interfaz de usuario pueden ser difíciles. Se necesita un poco de tiempo para escribir las pruebas de UI correctamente. Son mucho más lentos y frágiles que las pruebas unitarias porque cruzan los límites de las clases y los procesos, golpean el navegador, involucran elementos de la IU (por ejemplo, HTML, JavaScript) que cambian constantemente, llegan a la base de datos, al sistema de archivos y, potencialmente, a los servicios de red. Si alguna de estas partes móviles no toca bien, tienes una prueba rota; Pero esa es también la belleza de las pruebas de interfaz de usuario: prueban su sistema de extremo a extremo. Ninguna otra prueba le brinda tanta o tan completa cobertura. Las pruebas automatizadas de UI, si se hacen correctamente, podrían ser los mejores elementos de su conjunto de regresión.

¡Así que en los últimos proyectos, mis pruebas de UI han formado más del 80% de mis pruebas! También debo mencionar que estos proyectos en su mayoría han sido aplicaciones CRUD con poca lógica empresarial y, seamos sinceros, la gran mayoría de los proyectos de software se encuentran en esta categoría. La lógica de negocios aún debe ser probada por unidades; pero el resto de la aplicación se puede probar a fondo a través de la automatización de la interfaz de usuario.


Pruebas de IU Gone Wrong

Me gustaría referirme a lo que hice mal, lo que también parece ser muy típico entre los desarrolladores y evaluadores que comienzan con la automatización de la interfaz de usuario..

Entonces, ¿qué va mal y por qué? Muchos equipos inician la automatización de la interfaz de usuario con grabadores de pantalla. Si está realizando la automatización web con Selenium, lo más probable es que haya utilizado Selenium IDE. Desde la página de inicio de Selenium IDE:

El Selenium-IDE (Entorno de desarrollo integrado) es la herramienta que utiliza para desarrollar sus casos de prueba de Selenium.

Esta es en realidad una de las razones por las que las pruebas de UI se convierten en una experiencia horrible: descarga y enciende una grabadora de pantalla y navega a su sitio web y hace clic, hace clic, escribe, hace clic, escribe, tabula, escribe, tabula, escribe, hace clic y afirmar. Luego repites la grabación y funciona. ¡¡Dulce!! Así que exporta las acciones como un guión de prueba, colóquelo en su código, envuélvalo en una prueba, ejecute la prueba y vea que el navegador cobra vida ante sus ojos y que las pruebas se ejecuten sin problemas. Te emocionas mucho, compartes tus hallazgos con tus colegas y se los enseñas a tu jefe y ellos se emocionan mucho y se van: "Automatiza TODAS LAS COSAS"

Una semana después, tienes 10 pruebas de UI automatizadas y todo parece genial. Luego, la empresa le pide que reemplace el nombre de usuario con la dirección de correo electrónico, ya que ha causado cierta confusión entre los usuarios, y así lo hace. Entonces, al igual que con cualquier otro gran programador, ejecuta su conjunto de pruebas de interfaz de usuario, solo para encontrar que el 90% de sus pruebas están interrumpidas porque con cada prueba está ingresando al usuario con nombre de usuario y el nombre del campo ha cambiado y le toma dos horas reemplazar todos las referencias a nombre de usuario en tus pruebas con correo electrónico y volver a poner las pruebas en verde. Lo mismo sucede una y otra vez y en algún momento pasa horas al día reparando pruebas rotas: pruebas que no se rompieron porque algo salió mal con su código; pero debido a que cambió un nombre de campo en su base de datos / modelo o reestructuró su página ligeramente. Unas semanas más tarde, deja de ejecutar las pruebas debido a este enorme costo de mantenimiento, y llega a la conclusión de que las pruebas de IU son muy malas..

NO debe utilizar Selenium IDE ni ninguna otra grabadora de pantalla para desarrollar sus casos de prueba. Dicho esto, no es el grabador de pantalla lo que conduce a un conjunto de pruebas frágiles; Es el código que generan el que tiene problemas de mantenimiento inherentes. Muchos desarrolladores todavía terminan con una suite de prueba de IU frágil incluso sin usar grabadores de pantalla simplemente porque sus pruebas exhiben los mismos atributos.

Todas las pruebas en este artículo están escritas contra el sitio web de Mvc Music Store. El sitio web tiene algunos problemas que dificultan las pruebas de UI, así que porté el código y solucioné los problemas. Puede encontrar el código real contra el que estoy escribiendo estas pruebas en el repositorio de GitHub para este artículo aquí

Entonces, ¿cómo se ve una prueba de fragilidad? Se ve algo como esto:

clase BrittleTest [Test] public void Can_buy_an_Album_when_registered () var driver = Host.Instance.Application.Browser; driver.Navigate (). GoToUrl (driver.Url); driver.FindElement (By.LinkText ("Admin")). Haga clic en (); driver.FindElement (By.LinkText ("Register")). Haga clic en (); driver.FindElement (By.Id ("UserName")). Clear (); driver.FindElement (By.Id ("UserName")). SendKeys ("HJSimpson"); driver.FindElement (By.Id ("Password")). Clear (); driver.FindElement (By.Id ("Password")). SendKeys ("! 2345Qwert"); driver.FindElement (By.Id ("ConfirmPassword")). Clear (); driver.FindElement (By.Id ("ConfirmPassword")). SendKeys ("! 2345Qwert"); driver.FindElement (By.CssSelector ("input [type = \" submit \ "]")). Haga clic en (); driver.FindElement (By.LinkText ("Disco")). Haga clic en (); driver.FindElement (By.CssSelector ("img [alt = \" Le Freak \ "]")). Haga clic en (); driver.FindElement (By.LinkText ("Add to cart")). Click (); driver.FindElement (By.LinkText ("Checkout >>")). Haga clic en (); driver.FindElement (By.Id ("FirstName")). Clear (); driver.FindElement (By.Id ("FirstName")). SendKeys ("Homer"); driver.FindElement (By.Id ("LastName")). Clear (); driver.FindElement (By.Id ("LastName")). SendKeys ("Simpson"); driver.FindElement (By.Id ("Address")). Clear (); driver.FindElement (By.Id ("Address")). SendKeys ("742 Evergreen Terrace"); driver.FindElement (By.Id ("City")). Clear (); driver.FindElement (By.Id ("City")). SendKeys ("Springfield"); driver.FindElement (By.Id ("State")). Clear (); driver.FindElement (By.Id ("State")). SendKeys ("Kentucky"); driver.FindElement (By.Id ("PostalCode")) Clear (); driver.FindElement (By.Id ("PostalCode")). SendKeys ("123456"); driver.FindElement (By.Id ("Country")). Clear (); driver.FindElement (By.Id ("Country")). SendKeys ("United States"); driver.FindElement (By.Id ("Phone")). Clear (); driver.FindElement (By.Id ("Phone")). SendKeys ("2341231241"); driver.FindElement (By.Id ("Email")). Clear (); driver.FindElement (By.Id ("Email")). SendKeys ("[email protected]"); driver.FindElement (By.Id ("PromoCode")). Clear (); driver.FindElement (By.Id ("PromoCode")). SendKeys ("FREE"); driver.FindElement (By.CssSelector ("input [type = \" submit \ "]")). Haga clic en (); Assert.IsTrue (driver.PageSource.Contains ("Checkout Complete")); 

Usted puede encontrar el Prueba frágil clase aqui.

El host es una clase estática, con una sola propiedad estática: Ejemplo, que, en el momento de la creación de instancias, activa IIS Express en el sitio web bajo prueba y vincula Firefox WebDriver a la instancia del navegador. Cuando finaliza la prueba, cierra el navegador e IIS Express automáticamente.

Esta prueba activa un navegador web, va a la página de inicio del sitio web de Mvc Music Store, registra un nuevo usuario, busca un álbum, lo agrega al carrito y lo verifica.

Uno podría argumentar que esta prueba está haciendo demasiado y por eso es frágil; Pero el tamaño de esta prueba no es la razón por la que es frágil, es la forma en que está escrito lo que hace que sea una pesadilla mantener.

Existen diferentes escuelas de pensamiento sobre las pruebas de UI y cuánto debe cubrir cada prueba. Algunos creen que esta prueba está haciendo demasiado y otros piensan que una prueba debe cubrir un escenario real, de extremo a extremo, y considera que es una prueba perfecta (aparte de la capacidad de mantenimiento).

Entonces, ¿qué está mal con esta prueba?

  • Este es el código de procedimiento. Uno de los principales problemas de este estilo de codificación es la legibilidad, o la falta de ella. Si desea cambiar la prueba, o si se rompe porque una de las páginas involucradas ha cambiado, tendrá dificultades para decidir qué cambiar y dibujar una línea entre las secciones de funcionalidad; porque todo es una gran pila de código donde obtenemos el 'controlador' para encontrar un elemento en la página y hacer algo con él. Sin modularidad.
  • Es posible que esta única prueba no tenga mucha duplicación, pero sí algunas pruebas más como esta, y tendrá un montón de lógica y selector duplicados para interactuar con páginas web de diferentes pruebas. Por ejemplo Por.Id ("Nombre de usuario") El selector se duplicará en todas las pruebas que requieran registro, y driver.FindElement (By.Id ("UserName")). Clear () y driver.FindElement (By.Id ("UserName")). SendKeys ("") se duplican en cualquier lugar donde desee interactuar con el cuadro de texto UserName. Luego está el formulario de registro completo, y el formulario de pago, etc. que se repetirá en todas las pruebas que necesiten interactuar con ellos. Código duplicado conduce a pesadillas de mantenibilidad.
  • Hay una gran cantidad de cadenas mágicas en todas partes, lo que de nuevo es un problema de mantenibilidad..

Código de prueba es código!

También hay patrones que le permiten escribir más pruebas de interfaz de usuario mantenibles.

Al igual que su código real, tendrá que mantener sus pruebas. Entonces dales el mismo trato..

¿Qué hay en las pruebas que nos hacen pensar que podemos renunciar a la calidad en ellas? En todo caso, un conjunto de pruebas incorrecto en mi opinión es mucho más difícil de mantener que un código incorrecto. He tenido piezas defectuosas de código de trabajo en producción durante años que nunca se rompieron y nunca tuve que tocarlas. Claro que era feo y difícil de leer y mantener, pero funcionaba y no necesitaba cambios, por lo que el costo real de mantenimiento era cero. Sin embargo, la situación no es la misma para las pruebas incorrectas: porque las pruebas malas se romperán y corregirlas va a ser difícil. No puedo contar el número de veces que he visto a los desarrolladores evitar las pruebas porque piensan que escribir pruebas es una gran pérdida de tiempo porque se necesita mucho tiempo para mantener.

El código de prueba es el código: ¿aplica SRP en su código? Entonces deberías aplicarlo en tus pruebas también. ¿Es tu código DRY? Luego, seca tus pruebas también. Si no escribe buenas pruebas (UI o de otro tipo) perderá mucho tiempo manteniéndolas.

También hay patrones que le permiten escribir más pruebas de interfaz de usuario mantenibles. Estos patrones son independientes de la plataforma: he usado estas mismas ideas y patrones para escribir pruebas de UI para aplicaciones WPF y aplicaciones web escritas en ASP.Net y Ruby on Rails. Así que, independientemente de su pila de tecnología, debería poder hacer que sus pruebas de UI sean mucho más fáciles de mantener siguiendo unos simples pasos..

Presentando el patrón de objeto de página

Muchos de los problemas mencionados anteriormente están arraigados en la naturaleza procesal del script de prueba y la solución es fácil: Orientación a objetos.

El objeto de página es un patrón que se utiliza para aplicar la orientación de objetos a las pruebas de IU. Desde el wiki de Selenium:

Dentro de la interfaz de usuario de su aplicación web hay áreas con las que interactúan sus pruebas. Un objeto de página simplemente modela estos objetos como objetos dentro del código de prueba. Esto reduce la cantidad de código duplicado y significa que si la IU cambia, la solución solo debe aplicarse en un lugar.

La idea es que para cada página de su aplicación / sitio web desee crear un objeto de página. Los objetos de página son básicamente el equivalente de automatización de la interfaz de usuario de sus páginas web..

He seguido adelante y he reformulado la lógica y las interacciones del BrittleTest en algunos objetos de la página y he creado una nueva prueba que los utiliza en lugar de golpear directamente el controlador web. Puedes encontrar la nueva prueba aquí. El código se copia aquí para su referencia:

clase pública TestWithPageObject [Prueba] public void Can_buy_an_Album_when_registered () var registerPage = HomePage.Initiate () .GoToAdminForAnonymousUser () .GoToRegisterPage (); registerPage.Username = "HJSimpson"; registerPage.Email = "[email protected]"; registerPage.Password = "! 2345Qwert"; registerPage.ConfirmPassword = "! 2345Qwert"; var shippingPage = registerPage .SubmitRegistration () .SelectGenreByName ("Disco") .SelectAlbumByName ("Le Freak") .AddToCart () .Checkout (); shippingPage.FirstName = "Homer"; shippingPage.LastName = "Simpson"; shippingPage.Address = "742 Evergreen Terrace"; shippingPage.City = "Springfield"; shippingPage.State = "Kentucky"; shippingPage.PostalCode = "123456"; shippingPage.Country = "Estados Unidos"; shippingPage.Phone = "2341231241"; shippingPage.Email = "[email protected]"; shippingPage.PromoCode = "FREE"; var orderPage = shippingPage.SubmitOrder (); Assert.AreEqual (orderPage.Title, "Checkout Complete"); 

Es cierto que el cuerpo de la prueba no ha disminuido mucho en tamaño y, de hecho, tuve que crear siete nuevas clases para respaldar esta prueba. A pesar de las más líneas de código requeridas, simplemente solucionamos muchos problemas que tenía la prueba original de frágil (más sobre esto más adelante). Por ahora, vamos a profundizar un poco más en el patrón de objeto de página y lo que hicimos aquí.

Con el patrón Objeto de página, normalmente creas una clase de objeto de página por página web bajo prueba donde la clase modela y encapsula las interacciones con la página. Por lo tanto, un cuadro de texto en su página web se convierte en una propiedad de cadena en el objeto de página y para rellenar ese cuadro de texto, simplemente configure esa propiedad de texto en el valor deseado, en lugar de:

driver.FindElement (By.Id ("Email")). Clear (); driver.FindElement (By.Id ("Email")). SendKeys ("[email protected]");

podemos escribir:

registerPage.Email = "[email protected]";

dónde registroPágina Es una instancia de la clase RegisterPage. Una casilla de verificación en la página se convierte en una propiedad bool en el Objeto de página y marcar y desmarcar la casilla de verificación es solo una cuestión de configurar esa propiedad booleana como verdadera o falsa. Del mismo modo, un enlace en la página web se convierte en un método en el Objeto de página y al hacer clic en el enlace se convierte en llamar al método en el Objeto de página. Así que en lugar de:

driver.FindElement (By.LinkText ("Admin")). Haga clic en ();

podemos escribir:

homepage.GoToAdminForAnonymousUser ();

De hecho, cualquier acción en nuestra página web se convierte en un método en nuestro objeto de página y, en respuesta a la acción (es decir, al llamar al método en el objeto de página), obtiene una instancia de otro objeto de página que apunta a la página web que acaba de acceder. Para navegar, realice la acción (p. ej., enviar un formulario o hacer clic en un enlace). De esta manera puede encadenar fácilmente sus interacciones de vista en su script de prueba:

var shippingPage = registerPage .SubmitRegistration () .SelectGenreByName ("Disco") .SelectAlbumByName ("Le Freak") .AddToCart () .Checkout ();

Aquí, después de registrar al usuario, me llevan a la página de inicio (una instancia de su objeto de página es devuelta por Enviar registro método). Así que en la instancia de HomePage llamo SeleccioneGenreByName que hace clic en un enlace 'Disco' en la página que devuelve una instancia de AlbumBrowsePage y luego en esa página que llamo SeleccioneAlbumByName que hace clic en el álbum 'Le Freak' y devuelve una instancia de AlbumDetailsPage y así sucesivamente.

Lo admito: son muchas clases para lo que antes no era una clase; pero obtuvimos muchos beneficios de esta práctica. En primer lugar el código ya no es de procedimiento. Tenemos un modelo de prueba bien contenido donde cada objeto proporciona una buena encapsulación de interacción con una página. Por ejemplo, si algo cambia en su lógica de registro, el único lugar que tiene que cambiar es su clase RegisterPage en lugar de tener que pasar por todo su conjunto de pruebas y cambiar cada una de las interacciones con la vista de registro. Esta modularidad también proporciona una buena reutilización: puede reutilizar su ShoppingCartPage En todos lados necesitas interactuar con el carrito de compras. Así que en una práctica simple de pasar de un código de prueba de procedimiento a uno orientado a objetos, casi eliminamos tres de los cuatro problemas con la prueba de fragilidad inicial, que eran código de procedimiento, y lógica y duplicación de selector. Todavía tenemos un poco de duplicación, que solucionaremos en breve..

¿Cómo implementamos esos objetos de página? Un objeto de página en su raíz no es más que un envoltorio alrededor de las interacciones que tiene con la página. Aquí acabo de extraer las interacciones de UI de nuestras pruebas de fragilidad y colocarlas en sus propios objetos de página. Por ejemplo, la lógica de registro se extrajo en su propia clase llamada RegistroPágina que se veía así:

clase pública RegisterPage: Page Public HomePage SubmitRegistration () return NavigateTo(By.CssSelector ("input [type = 'submit']"));  cadena pública Nombre de usuario set Ejecutar (By.Name ("UserName"), e => e.Clear (); e.SendKeys (value););  cadena pública Correo electrónico set Ejecutar (By.Name ("Email"), e => e.Clear (); e.SendKeys (value););  cadena pública ConfirmPassword set Execute (By.Name ("ConfirmPassword"), e => e.Clear (); e.SendKeys (value););  cadena pública Contraseña set Ejecutar (By.Name ("Password"), e => e.Clear (); e.SendKeys (value);); 

He creado un Página Superclase que cuida algunas cosas, como Navegar a que ayuda a navegar a una nueva página realizando una acción y Ejecutar que ejecuta algunas acciones sobre un elemento. los Página clase se veía como

clase pública Página protegido RemoteWebDriver WebDriver get return Host.Instance.WebDriver;  título de cadena pública get return WebDriver.Title;  público TPage NavigateTo(By by) donde TPage: Page, new () WebDriver.FindElement (by) .Click (); devuelve Activator.CreateInstance();  public void Execute (By by, Action action) var element = WebDriver.FindElement (by); acción (elemento); 

En el Prueba frágil, Para interactuar con un elemento que hicimos. FindElement Una vez por acción. los Ejecutar El método, además de abstraer la interacción del controlador web, tiene un beneficio adicional que permite seleccionar un elemento, lo que podría ser una acción costosa, una vez y realizar múltiples acciones:

driver.FindElement (By.Id ("Password")). Clear (); driver.FindElement (By.Id ("Password")). SendKeys ("! 2345Qwert");

fue reemplazado por:

Ejecutar (By.Name ("Password"), e => e.Clear (); e.SendKeys ("! 2345Qwert");)

Echando un segundo vistazo a la RegistroPágina Objeto de la página anterior todavía tenemos un poco de duplicación allí. El código de prueba es código y no queremos duplicación en nuestro código; así que vamos a refactorizar eso. Podemos extraer el código requerido para completar un cuadro de texto en un método en el Página clase y simplemente llamarlo desde objetos de página. El método podría ser implementado como:

public void SetText (string elementName, string newText) Execute (By.Name (elementName), e => e.Clear (); e.SendKeys (newText);); 

Y ahora las propiedades en RegistroPágina puede reducirse a:

cadena pública Nombre de usuario set SetText ("Nombre de usuario", valor); 

También puede crear una API fluida para que el instalador lea mejor (por ejemplo,. Rellenar ("Nombre de usuario"). Con (valor)) pero te lo dejo a ti.

No estamos haciendo nada extraordinario aquí. Simplemente refactorizando nuestro código de prueba como siempre lo hemos hecho para nuestro código errrr, "otro"!!

Puedes ver el código completo de Página y RegistroPágina clases aquí y aquí.

Objeto de página fuertemente tipado

Resolvimos los problemas de procedimiento con la prueba de fragilidad, que hizo que la prueba fuera más legible, modular, DRY y mantenible de manera efectiva. Hay un último problema que no solucionamos: todavía hay muchas cadenas mágicas en todas partes. No es una pesadilla, pero sigue siendo un problema que podríamos solucionar. Introduzca objetos de página fuertemente tipados!

Este enfoque es práctico si está utilizando un marco MV * para su interfaz de usuario. En nuestro caso estamos utilizando ASP.Net MVC..

Echemos otro vistazo a la RegistroPágina:

clase pública RegisterPage: Page Public HomePage SubmitRegistration () return NavigateTo(By.CssSelector ("input [type = 'submit']"));  cadena pública Nombre de usuario set SetText ("Nombre de usuario", valor);  cadena pública Correo electrónico set SetText ("Correo electrónico", valor);  cadena pública ConfirmPassword set SetText ("ConfirmPassword", valor);  cadena pública Contraseña set SetText ("Contraseña", valor); 

Esta página modela la vista Registro en nuestra aplicación web (solo copiando la parte superior aquí para su conveniencia):

@model MvcMusicStore.Models.RegisterModel @ ViewBag.Title = "Register"; 

Hmmm, que es eso RegistrarseModelo ¿ahí? Es el modelo de visualización de la página: METRO en el MVC. Aquí está el código (quité los atributos para reducir el ruido):

clase pública RegisterModel public string UserName get; conjunto;  cadena pública Correo electrónico get; conjunto;  cadena pública Contraseña obtener; conjunto;  cadena pública ConfirmPassword get; conjunto; 

Eso se ve muy familiar, ¿no? Tiene las mismas propiedades que el RegistroPágina clase que no es sorprendente considerando RegistroPágina fue creado en base a este modelo de vista y vista. Veamos si podemos aprovechar los modelos de vista para simplificar nuestros objetos de página..

He creado una nueva Página superclase pero uno genérico. Puedes ver el código aquí:

página de clase pública : Página donde TViewModel: class, new () public void FillWith (TViewModel viewModel, IDictionary> propertyTypeHandling = null) // eliminado por brevedad

los Página subclases de clase lo viejo Página Clase y proporciona toda su funcionalidad; Pero también tiene un método extra llamado Llenar con que llena la página con la instancia de modelo de vista proporcionada! Así que ahora mi RegistroPágina la clase se parece a

clase pública RegisterPage: página public HomePage CreateValidUser (modelo RegisterModel) FillWith (modelo); volver NavigateTo(By.CssSelector ("input [type = 'submit']")); 

Dupliqué todos los objetos de la página para mostrar ambas variaciones y también para hacer que el código base sea más fácil de seguir para usted; Pero en realidad necesitarás una clase para cada objeto de página..

Después de convertir los objetos de mi página a objetos genéricos, ahora la prueba parece:

public class StronglyTypedPageObjectWithComponent [Test] public void Can_buy_an_Album_when_registered () var ordersPage = HomePage.). Freak ") .AddAlbumToCart () .Checkout () .SubmitShippingInfo (ObjectMother.CreateShippingInfo ()," Free "); Assert.AreEqual ("Checkout Complete", pedidaPágina.título); 

Eso es todo, toda la prueba! Mucho más legible, SECO y mantenible, ¿no es así??

los ObjectMother La clase que estoy usando en la prueba es una Object Mother que proporciona datos de prueba (el código se puede encontrar aquí), nada del otro mundo:

clase pública ObjectMother public static Order CreateShippingInfo () var shippingInfo = new Order FirstName = "Homer", LastName = "Simpson", Address = "742 Evergreen Terrace", City = "Springfield", State = "Kentucky", PostalCode = "123456", País = "Estados Unidos", Teléfono = "2341231241", Correo electrónico = "[email protected]"; volver shippingInfo;  public static RegisterModel CreateRegisterModel () var model = new RegisterModel UserName = "HJSimpson", Email = "[email protected]", Password = "! 2345Qwert", ConfirmPassword = "! 2345Qwert"; modelo de retorno 

No te detengas en el objeto Page

Algunas páginas web son muy grandes y complejas. Anteriormente dije que el código de prueba es el código y debemos tratarlo como tal. Normalmente dividimos las páginas web grandes y complejas en componentes más pequeños y, en algunos casos, reutilizables (parciales). Esto nos permite componer una página web a partir de componentes más pequeños y más manejables. Deberíamos hacer lo mismo para nuestras pruebas. Para ello podemos utilizar los componentes de la página..

Un componente de página es bastante parecido a un objeto de página: es una clase que encapsula la interacción con algunos elementos en una página. La diferencia es que interactúa con una pequeña parte de una página web: modela un control de usuario o una vista parcial, por así decirlo. Un buen ejemplo para un componente de página es una barra de menú. Una barra de menú generalmente aparece en todas las páginas de una aplicación web. Realmente no desea seguir repitiendo el código requerido para interactuar con el menú en cada objeto de una sola página. En su lugar, puede crear un componente de página de menú y utilizarlo desde los objetos de su página. También puede utilizar componentes de página para tratar con cuadrículas de datos en sus páginas, y para ir un paso más allá, el componente de la misma página de cuadrícula podría estar compuesto de componentes de página de fila de cuadrícula. En el caso de Mvc Music Store podríamos tener un TopMenuComponent y un SideMenuComponent y úsalos desde nuestro Página principal.

Al igual que en su aplicación web, también podría crear, digamos, LayoutPage objeto de página que modela su diseño / página maestra y lo utiliza como una superclase para todos sus otros objetos de página. La página de diseño se compondría de componentes de la página del menú para que todas las páginas puedan golpear los menús. Supongo que una buena regla general sería tener un componente de página por vista parcial, un objeto de página de diseño por diseño y un objeto de página por página web. De esa manera usted sabe que su código de prueba es tan granular y está bien compuesto como su código.

Algunos marcos para pruebas de interfaz de usuario

Lo que mostré anteriormente fue una muestra muy simple y artificial con algunas clases de apoyo como infraestructura para las pruebas. En realidad, los requisitos para las pruebas de UI son mucho más complejos que eso: existen controles e interacciones complejos, debe escribir y leer desde sus páginas, debe lidiar con las latencias de red y tener control sobre AJAX y otras interacciones de Javascript. Necesito disparar diferentes navegadores, etc. que no expliqué en este artículo. Aunque es posible codificar todo esto, el uso de algunos marcos puede ahorrarle mucho tiempo. Aquí están los marcos que recomiendo altamente:

Marcos para .Net:

  • Seleno es un proyecto de código abierto de TestStack que le ayuda a escribir pruebas automatizadas de UI con Selenium. Se enfoca en el uso de Objetos de página y Componentes de página y al leer y escribir en páginas web utilizando modelos de vista fuertemente tipados. Si te gustó lo que hice en este artículo, también te gustará Seleno, ya que la mayoría del código que se muestra aquí fue tomado del código base de Seleno..
  • White es un marco de código abierto de TestStack para automatizar aplicaciones cliente enriquecidas basadas en plataformas Win32, WinForms, WPF, Silverlight y SWT (Java)..

Divulgación: soy co-fundador y miembro del equipo de desarrollo en la organización TestStack.

Marcos para Ruby:

  • Capybara es un marco de prueba de aceptación para aplicaciones web que le ayuda a probar aplicaciones web simulando cómo un usuario real interactuaría con su aplicación.
  • Poltergeist es un conductor de Capybara. Le permite ejecutar sus pruebas de Capybara en un navegador WebKit sin cabeza, provisto por PhantomJS.
  • page-object (no he usado personalmente esta gema) es una gema simple que ayuda a crear objetos de página flexibles para probar aplicaciones basadas en navegador. El objetivo es facilitar la creación de capas de abstracción en sus pruebas para desacoplar las pruebas del elemento que están probando y proporcionar una interfaz simple a los elementos en una página. Funciona tanto con watir-webdriver como con selenium-webdriver..

Conclusión

Comenzamos con una experiencia típica de automatización de UI, explicamos por qué fallan las pruebas de UI, proporcionamos un ejemplo de una prueba frágil y discutimos sus problemas y los resolvimos utilizando algunas ideas y patrones..

Si desea obtener un punto de este artículo, debe ser: El código de prueba es el código. Si lo piensa, todo lo que hice en este artículo fue aplicar la buena codificación y las prácticas orientadas a objetos que ya conoce a una prueba de UI..

Todavía hay mucho que aprender acerca de las pruebas de UI y trataré de discutir algunos de los consejos más avanzados en un artículo futuro..

Feliz prueba!