Siempre que en algún evento o en alguna formación muestro las pruebas web de Team System, surge una pregunta inevitablemente: ¿y esto no lo hay para aplicaciones Windows?… Mi respuesta tradicionalmente ha sido: No, no hay nada… y luego venía la explicación ‘oficial’ habitual: Es que tu aplicación debe tener una capa de interfaz muy fina y claro con las pruebas unitarias debería ser suficiente, no pasa nada porque algo de código de tu aplicación, relacionado con la interfaz de usuario se quede sin probar automáticamente, y bla, bla, bla…
El otro día uno de los desarrolladores de uno de los equipos para los que estoy haciendo mentoring sobre Scrum y Team System me decía lo siguiente: Según lo propugnado por las metodologías ágiles debemos probar todo lo que es susceptible de fallar y debemos automatizar toda prueba que sea automatizable, así que Rodrigo, tu respuesta no me deja contento… y la verdad es que tiene toda la razón. Si bien es cierto que existen herramientas de terceros que permiten grabar pruebas de interfaz de usuario estas son caras o muy caras, así que esta ocasión no nos sirven. Aquí termino la historia…
Es cierto que está respuesta cambiará notablemente con VSTS ‘Rosario’, que integrará de serie la posibilidad de grabar pruebas de interfaz de usuario, pero también es cierto que hasta Rosario nos queda un trecho.
Pero yo no me suelo rendir fácilmente, quien haya jugado conmigo a pelota mano lo sabe, y después de la conversación anterior he estado dándole vueltas al tema y tengo una nueva respuesta… no exenta de complejidades, pero una respuesta al fin y al cabo. La respuesta no pasa por grabar la pruebas al estilo de como se graban las pruebas web de VSTS, sino por escribir código que automatice las pruebas de interfaz de usuario.
Este enfoque es mucho más flexible que el grabar test, pues cuando nuestra interfaz de usuario sufre cambios en mi opinión es mucho más simple modificar código que grabar de nuevo los test. De hecho, los equipos que tienen baterías de pruebas web de tamaño considerable suelen elegir generar el código correspondiente a la prueba y modificarlo según su interfaz de usuario va cambiando. Aun recuerdo como en una visita a Redmond con ocasión de un MVP Summit, tuve gracias a Ricardo Varela, la posibilidad pasar un rato con un ingeniero de pruebas del equipo de Windows Mobile. Me enseño las pruebas automatizadas de interfaz de usuario que escribía ‘a manele’ y ejecutaba contra una ‘granja’ de diferentes modelos de PDA. Desde ese momento, tuve claro que automatizar pruebas de interfaz de usuario es posible y que escribir código para ello es un camino viable.
Con idea de saber si se puede andar este camino integrando nuestras pruebas con las pruebas unitarias de VSTS y para hacerme idea del la magnitud de esfuerzo que este camino exige he hecho un pequeño piloto. La idea es simple: automatizar el testeo de algunas acciones de la calculadora de Windows. La investigación previa me llevo a este artículo de MSDN Magazine: Test Run: The Microsoft UI Automation Library que me mostro el camino hacia la automatización de las pruebas de interfaz.
Así que vamos usar una útil y desconocida librería de Microsoft: UI Automation Library. Esta librería nos permite automatizar acciones sobre cualquier interfaz Windows sea está Win32, Windows Forms, Web o WPF y está orientada a soportar accesibilidad y las pruebas de interfaz de usuario. Para usarla será necesario añadir una referencia a UIAutomationClient y a UIAutomationTypes y hacer un ‘using’ de System.Windows.Automation. A partir de este momento tendremos acceso a las clases de la librería de automatización de la interfaz de usuario.
Usando esta librería es muy simple crear pruebas unitarias que automaticen nuestras pruebas de interfaz de usuario. Estas pruebas unitarias se pueden ejecutar desde VSTS sin problemas, pero no se podrán ejecutar como parte de una build de TFS, pues necesitan, lógicamente mostrar interfaz de usuario.
Os dejo el código de las pruebas hechas sobre la calculadora, creo que se explican por sí mismas. Son un ejemplo simple de como provocar acciones de interfaz de usuario usando UI Automation Library. También podéis descargar el código si lo deseáis.
using System.Diagnostics;
using System.Windows.Automation;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UITestingSample
{
/// <summary>
/// </summary>
[TestClass]
public class UITestSample
{
static AutomationElement _mainWindow;
/// <summary>
/// Initilizes objects needed for testing
/// </summary>
/// <param name=»testContext»>Test context</param>
/// <remarks>
/// Get the calculator’s main window automation element and store
/// it in a static private field
/// </remarks>
[ClassInitialize()]
public static void ClassInitialize(TestContext testContext)
{
Process[] processes = Process.GetProcessesByName(«calc»);
if (processes.Length != 0)
{
_mainWindow =
AutomationElement.FromHandle(processes[0].MainWindowHandle);
}
else
{
Assert.Inconclusive(«Windows Calculator must be running»);
}
}
/// <summary>
/// Test for sum
/// </summary>
[TestMethod]
public void SumTest()
{
InvokeButton(«C»);
InvokeButton(«1»);
InvokeButton(«+»);
InvokeButton(«1»);
InvokeButton(«=»);
Assert.AreEqual(GetDisplayText(), «2, «);
}
/// <summary>
/// Test for square root
/// </summary>
[TestMethod]
public void SqrtTest()
{
InvokeButton(«C»);
InvokeButton(«9»);
InvokeButton(«sqt»);
Assert.AreEqual(GetDisplayText(), «3, «);
}
/// <summary>
/// This function get the automation element corresponding to a button from its caption
/// </summary>
/// <param name=»caption»>Button’s caption</param>
/// <returns>The automation element corresponding to the button</returns>
private AutomationElement GetButtonByCaption(string caption)
{
return _mainWindow.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.NameProperty, caption));
}
/// <summary>
/// This function click a button provided its caption
/// </summary>
/// <param name=»caption»>Button’s caption</param>
private void InvokeButton(string caption)
{
AutomationElement ae = GetButtonByCaption(caption);
((InvokePattern)(ae.GetCurrentPattern(InvokePattern.Pattern))).Invoke();
}
/// <summary>
/// Get the text displayed by the calculator
/// </summary>
/// <returns></returns>
private string GetDisplayText()
{
AutomationElement ae =
_mainWindow.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
return (string)ae.GetCurrentPropertyValue(ValuePattern.ValueProperty);
}
}
}
La conclusión de mi pequeño piloto es que es plenamente posible escribir pruebas de interfaz de usuario e integrarlas dentro de las pruebas de usuario de VSTS. También es cierto que para que este esfuerzo sea viable, debemos poner mucho enfoque en crear una serie de funciones de ayuda que enmascaren el tener que lidiar con la UI Automation Library. La gran pregunta es ¿merece la pena el esfuerzo?… supongo que todo depende de la cantidad de código que tengamos en nuestra interfaz de usuario. Si logramos minimizar esta variable, como debemos perseguir en toda buena arquitectura, creo que el esfuerzo no merecerá la pena.