Pruebas eficientes de unidad de JavaScript

Automatice las pruebas de código de JavaScript en los navegadores

El código de JavaScript que se ejecuta en un navegador no necesariamente significa que funcionará en otros. Sin hacer pruebas de unidad en este código, las organizaciones pagan dinero para probar y volver a probar aplicaciones web al decidir actualizar o soportar nuevos navegadores. En este artículo, aprenda cómo las pruebas de unidad eficientes de su JavaScript pueden reducir los costos de pruebas y facilitarle el soporte de más navegadores.

Hazem Saleh, Staff Software Engineer, IBM  

Photo of Hazem SalehHazem Saleh tiene siete años de experiencia en Java Enterprise y tecnologías de código abierto. Es un confirmador de Apache y el autor de "La Guía Definitiva para Apache MyFaces y Facelets" (Apress). También es autor de muchos artículos técnicos, un autor contribuyente de developerWorks y un ponente técnico en conferencias locales e internacionales. Ha hablado en JavaOne y en IBM Regional Technical Exchange. Hazem es un Experto den la Materia en el Tema de la Web 2.0 y es un Ingeniero de Software del Personal de IBM Egypt.



24-02-2012

Un ejemplo de código de JavaScript que no funciona

Uno de los más grandes retos que enfrentan las aplicaciones web es el soporte de navegadores web con distintas versiones. El código de JavaScript que se ejecuta en Safari no necesariamente funciona en Windows® Internet Explorer (IE), Firefox o Google Chrome. Este reto es heredado de la falta de pruebas en el código de JavaScript que está viviendo en la capa de presentación desde el día uno. Sin hacer pruebas de unidad en este código, las organizaciones pueden pagar pruebas repetidas de aplicaciones web después de actualizar o soportar nuevos navegadores. Este artículo le muestra cómo reducir los costos de pruebas usando pruebas de unidad eficientes para código de JavaScript.

Uno de los casos de uso común es tener una validación de JavaScript de formulario de inicio de sesión. Considere el formulario en el Listado 1.

Listado 1. El formulario de inicio de sesión
<FORM>
	<table>
		<tr>
			<td>Username</td>
			<td><input type="text" id="username"/></td>
			<td><span id="usernameMessage"></span></td>
		</tr>
		<tr>
			<td>Password</td>
			<td><input type="password" id="password"/></td>
			<td><span id="passwordMessage"></span></td>
		</tr>	
		<tr>
			<td><input type="button" onclick="new appnamespace.
			ApplicationUtil().validateLoginForm()" value="Submit"/></td>
		</tr>
	</table>
</FORM>

El formulario es simple. Consiste en campos de nombre de usuario y contraseña. Al hacer clic en el botón Submit, una validación de formulario específica es realizada mediante ApplicationUtil. Este es un objeto de JavaScript responsable de validar el formulario de HTML. El Listado 2 muestra el código del objeto ApplicationUtil .

Listado 2. El código roto del objeto ApplicationUtil
appnamespace = {};

appnamespace.ApplicationUtil = function() {};

appnamespace.ApplicationUtil.prototype.validateLoginForm =  function(){
	var error = true;
	document.getElementById("usernameMessage").innerText = "";
	document.getElementById("passwordMessage").innerText = "";	

	if (!document.getElementById("username").value) {
		document.getElementById("usernameMessage").innerText = 
		"This field is required";
		error = false;
	}
	
	if (!document.getElementById("password").value) {
		document.getElementById("passwordMessage").innerText = 
		"This field is required";
		error = false;
	}		

	return error;		
};

En el Listado 2, el objeto ApplicationUtil proporciona una validación simple. Verifica que los campos de nombre de usuario y contraseña sean llenados. Si un campo está vacío, un mensaje de error que dice This field is required aparece.

Aunque el código anterior funcionará en Internet Explorer 8 y Safari 5.1, no funcionará en Firefox 3.6, ya que la propiedad innerText no está soportada en Firefox. Frecuentemente, el principal problema (en el código anterior y en otro código de JavaScript similar) es que no es fácil detectar si el código de JavaScript escrito es compatible con todos los navegadores.

Una solución para este problema es tener pruebas de unidad automatizadas que verifiquen que el código sea correcto en todos los navegadores.


JsTestDriver

Una de las mejores infraestructuras de pruebas de unidad de JavaScript es la biblioteca JsTestDriver, que proporciona pruebas para código de JavaScript en todos los navegadores. La Figura 1 muestra la arquitectura de JsTestDriver.

Figura 1. Arquitectura de JsTestDriver
Arquitectura de JsTestDriver

jsTestDriver es de código abierto

jsTestDriver es un proyecto de código abierto bajo la licencia de Apache 2.0, alojado en Google Code, un repositorio de proyectos similar a SourceForge. Los desarrolladores pueden crear y gestionar proyectos públicos en este repositorio siempre y cuando usen una de las licencias aprobadas por Open Source Initiative.

Hay muchas otras herramientas de pruebas de unidad de JavaScript disponibles. Vea la sección recursos a continuación para obtener referencias sobre otras herramientas, como Dojo Objective Harness (DOH).

El servidor es responsable de cargar el código ejecutor de casos de prueba de JavaScript en los distintos navegadores una vez que son capturados. El navegador puede ser capturado mediante la línea de comandos o al apuntar el navegador hacia la URL del servidor. Una vez que el navegador es capturado, es llamado un navegador esclavo. El servidor carga el código de JavaScript, ejecuta los casos de prueba en cada navegador y retorna los resultados al cliente.

El cliente (línea de comandos) necesita dos elementos principales:

  1. Archivos JavaScript — archivos de origen y archivos de prueba, y
  2. Archivo de configuración — para organizar la carga de los archivos de origen y los archivos de prueba.

Esta arquitectura es flexible, permitiendo que un solo servidor capture cualquier número de navegadores desde otras máquinas en la red. Por ejemplo, puede ser útil si su código se está ejecutando en Linux y desea ejecutar sus casos de prueba en Microsoft Internet Explorer en otra máquina de Windows.

Para comenzar a trabajar con la biblioteca JsTestDriver, descargue la versión más reciente de JsTestDriver 1.3.2.


Escribir el código de pruebas de unidad

Ahora, escribamos los casos de prueba de JavaScript. Para el propósito de la simplicidad, probaré los siguientes casos:

  • Un valor vacío para nombre de usuario y un valor vacío para contraseña.
  • Un valor vacío para nombre de usuario y un valor no vacío para contraseña.
  • Un valor no vacío para nombre de usuario y un valor vacío para contraseña.

El Listado 3 muestra parte del código del objeto ApplicationUtilTest que representa el objeto TestCase.

Listado 3. Parte del código del objeto ApplicationUtilTest
ApplicationUtilTest = TestCase("ApplicationUtilTest");

ApplicationUtilTest.prototype.setUp = function () {
/*:DOC += <FORM action=""><table><tr><td>Username</td><td>
<input type="text" id="username"/></td><td><span id="usernameMessage">
</span></td></tr><tr><td>Password</td><td>
<input type="password" id="password"/></td><td><span id="passwordMessage"
></span></td></tr></table></FORM>*/
};

ApplicationUtilTest.prototype.testValidateLoginFormBothEmpty = function () {
	var applicationUtil = new appnamespace.ApplicationUtil();
	
	/* Simulate empty user name and password */
	document.getElementById("username").value = "";
	document.getElementById("password").value = "";	
	
	applicationUtil.validateLoginForm();
	
	assertEquals("Username is not validated correctly!", "This field is required", 
	document.getElementById("usernameMessage").innerHTML);
	assertEquals("Password is not validated correctly!", "This field is required", 
	document.getElementById("passwordMessage").innerHTML);	
};

El objeto ApplicationUtilTest es creado mediante el objeto JsTestDriver TestCase . Si está familiarizado con la infraestructura de JUnit, estará familiarizado con los métodos setUp y testXXX . El método setUp es usado para inicializar el caso de prueba. Para este ejemplo, lo usé para declarar el fragmento de HTML que será usado para el resto de los métodos de caso de prueba.

La anotación DOC es una convención de JsTestDriver fácilmente usada para declarar un fragmento de HTML.

En el método testValidateLoginFormBothEmpty , un objeto ApplicationUtil es creado para usarse dentro del método de caso de prueba. El código entonces simula el ingreso de un nombre de usuario y contraseña vacíos al recuperar sus elementos de DOM y establecer sus valores como valores vacíos. El método validateLoginForm es llamado para realizar la validación de formulario real. Finalmente, el método assertEquals es llamado para asegurarse de que el mensaje en los elementos usernameMessage y passwordMessage sean correctos, siendo este: This field is required.

En JsTestDriver, puede usar las siguientes construcciones:

  • fail("msg") - indica que la prueba debe fallar y el parámetro de mensaje será mostrado como un mensaje de error.
  • assertTrue("msg", real)— hace valer que el parámetro real es verdadero. Si no es así, el parámetro de mensaje será mostrado como un mensaje de error.
  • assertFalse("msg", real)— hace valer que el parámetro real es falso. Si no es así, el parámetro de mensaje será mostrado como un mensaje de error.
  • assertSame("msg", esperado, real)— hace valer que el parámetro real es el mismo que el parámetro esperado. Si no es así, el parámetro de mensaje será mostrado como un mensaje de error.
  • assertNotSame("msg", esperado, real)— hace valer que el parámetro real no es el mismo que el parámetro esperado. Si no es así, el parámetro de mensaje será mostrado como un mensaje de error.
  • assertNull("msg", real)— hace valer que el parámetro real es nulo. Si no es así, el parámetro de mensaje será mostrado como un mensaje de error.
  • assertNotNull("msg", real)— hace valer que el parámetro real no es nulo. Si no es así, el parámetro de mensaje será mostrado como un mensaje de error.

El código de los otros métodos incluye otros casos de prueba. El Listado 4 muestra el código completo del objeto de caso de prueba.

Listado 4. El código completo del objeto ApplicationUtil
ApplicationUtilTest = TestCase("ApplicationUtilTest");

ApplicationUtilTest.prototype.setUp = function () {
/*:DOC += <FORM action=""><table><tr><td>Username</td><td>
<input type="text" id="username"/></td><td><span id="usernameMessage">
</span></td></tr><tr><td>Password</td><td>
<input type="password" id="password"/></td><td><span id="passwordMessage"
></span></td></tr></table></FORM>*/
};

ApplicationUtilTest.prototype.testValidateLoginFormBothEmpty = function () {
	var applicationUtil = new appnamespace.ApplicationUtil();
	
	/* Simulate empty user name and password */
	document.getElementById("username").value = "";
	document.getElementById("password").value = "";	
	
	applicationUtil.validateLoginForm();
	
	assertEquals("Username is not validated correctly!", "This field is required", 
	document.getElementById("usernameMessage").innerHTML);
	assertEquals("Password is not validated correctly!", "This field is required", 
	document.getElementById("passwordMessage").innerHTML);	
};

ApplicationUtilTest.prototype.testValidateLoginFormWithEmptyUserName = function () {
	var applicationUtil = new appnamespace.ApplicationUtil();
	
	/* Simulate empty user name and password */
	document.getElementById("username").value = "";
	document.getElementById("password").value = "anyPassword";	
	
	applicationUtil.validateLoginForm();
	
	assertEquals("Username is not validated correctly!", 
	"This field is required", document.getElementById("usernameMessage").innerHTML);
	assertEquals("Password is not validated correctly!", 
	"", document.getElementById("passwordMessage").innerHTML);	
};

ApplicationUtilTest.prototype.testValidateLoginFormWithEmptyPassword = function () {
	var applicationUtil = new appnamespace.ApplicationUtil();
	
	document.getElementById("username").value = "anyUserName";
	document.getElementById("password").value = "";	
	
	applicationUtil.validateLoginForm();
	
	assertEquals("Username is not validated correctly!", 
	"", document.getElementById("usernameMessage").innerHTML);
	assertEquals("Password is not validated correctly!", 
	"This field is required", document.getElementById("passwordMessage").
	innerHTML);	
};

Configurar los distintos navegadores con las pruebas

Una de las prácticas recomendadas para probar código de JavaScript es poner el código de origen de JavaScript en una carpeta distinta al código de prueba de JavaScript. Para el ejemplo mostrado en la Figura 2, nombré la carpeta de origen de JavaScript "js-src" y la carpeta de prueba de JavaScript "js-test", bajo la carpeta padre "js".

Figura 2. La estructura de la carpeta de pruebas de JavaScript
La estructura de la carpeta de pruebas de JavaScript

Después de estructurar las carpetas de origen y de prueba, tiene que proporcionar el archivo de configuración. De forma predeterminada, el ejecutor JsTestDriver busca un archivo de configuración llamado jsTestDriver.conf. Puede cambiar el nombre del archivo de configuración desde la línea de comandos. El Listado 5 muestra el contenido del archivo de configuración de JsTestDriver .

Listado 5. El contenido del archivo de configuración de JsTestDriver
server: http://localhost:9876

load:
  - js-src/*.js
  - js-test/*.js

El archivo de configuración está escrito en el formato de YAML. La directiva server especifica la dirección del servidor de prueba, mientras que la directiva load indica qué archivos de JavaScript cargar en los navegadores y en qué orden.

Ahora, probemos la clase del caso de prueba en IE, Firefox y Safari.

Para ejecutar las clases del caso de prueba, necesitamos iniciar el servidor. El servidor de JsTestDriver puede ser iniciado usando la siguiente línea de comandos:

java -jar JsTestDriver-1.3.2.jar --port 9876 --browser "[Firefox Path]",
          "[IE Path]","[Safari Path]"

Usando esta línea de comandos, el servidor se iniciará en el Puerto 9876 y capturará los navegadores de Firefox, IE y Safari en su máquina.

Después de lanzar y capturar los navegadores, puede ejecutar las clases del caso de prueba al usar la siguiente línea de comandos:

java -jar JsTestDriver-1.3.2.jar --tests all

Después de ejecutar el comando, verá los primeros resultados de ejecución, como se muestra en el Listado 6.

Listado 6. Los Primeros Resultados de Ejecución
Total 9 tests (Passed: 6; Fails: 3; Errors: 0) (16.00 ms)
  Firefox 3.6.18 Windows: Run 3 tests (Passed: 0; Fails: 3; Errors 0) (8.00 ms)
    ApplicationUtilTest.testValidateLoginFormBothEmpty failed (3.00 ms): 
	AssertError: Username is not validated correctly! expected "This field 
	is required" but was "" Error("Username is not validated correctly! 
	expected \"This field is required\" but was \"\"")@:0()@http://localhost
	:9876/test/js-test/TestApplicationUtil.js:16

    ApplicationUtilTest.testValidateLoginFormWithEmptyUserName failed (3.00 ms): 
	AssertError: Username is not validated correctly! expected "This field is 
	required" but was "" Error("Username is not validated correctly! expected 
	\"This field is required\" but was \"\"")@:0()@http://localhost:9876/test
	/js-test/TestApplicationUtil.js:29

    ApplicationUtilTest.testValidateLoginFormWithEmptyPassword failed (2.00 ms): 
	AssertError: Password is not validated correctly! expected "This field is 
	required" but was "" Error("Password is not validated correctly! expected 
	\"This field is required\" but was \"\"")@:0()@http://localhost:9876/test/
	js-test/TestApplicationUtil.js:42
	
  Safari 534.50 Windows: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (2.00 ms)
  Microsoft Internet Explorer 8.0 Windows: Run 3 tests (Passed: 3; Fails: 0; 
  Errors 0) (16.00 ms)
Tests failed: Tests failed. See log for details.

Observe en el Listado 6 que el principal problema es con Firefox. Las pruebas se ejecutaron exitosamente en Internet Explorer y Safari.


Corrija el código de JavaScript y vuelva a ejecutar los casos de prueba

Corrijamos el código roto de JavaScript. Usaremos innerHTML en lugar de innerText. El Listado 7 muestra el código corregido del objeto ApplicationUtil .

Listado 7. El código corregido del objeto ApplicationUtil
appnamespace = {};

appnamespace.ApplicationUtil = function() {};

appnamespace.ApplicationUtil.prototype.validateLoginForm =  function(){
	var error = true;
	document.getElementById("usernameMessage").innerHTML = "";
	document.getElementById("passwordMessage").innerHTML = "";	

	if (!document.getElementById("username").value) {
		document.getElementById("usernameMessage").innerHTML = 
		"This field is required";
		error = false;
	}
	
	if (!document.getElementById("password").value) {
		document.getElementById("passwordMessage").innerHTML = 
		"This field is required";
		error = false;
	}		

	return error;		
};

Vuelva a ejecutar el objeto de caso de prueba usando el argumento de línea de comandos --test all . El Listado 8 muestra los segundos resultados de ejecución.

Listado 8. Los segundos resultados de ejecución
Total 9 tests (Passed: 9; Fails: 0; Errors: 0) (9.00 ms)
  Firefox 3.6.18 Windows: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (9.00 ms)
  Safari 534.50 Windows: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (5.00 ms)
  Microsoft Internet Explorer 8.0 Windows: Run 3 tests (Passed: 3; Fails: 0; Errors 0) 
  (0.00 ms)

Como se muestra en el Listado 8, el código de JavaScript ahora está funcionando bien en IE, Firefox y Safari.


Conclusión

En este artículo, ha aprendido cómo probar un código de aplicación de JavaScript en distintos navegadores usando una de las herramientas de pruebas de unidad de JavaScript más poderosas (JsTestDriver). Usted sabe lo que es JsTestDriver, cómo configurarlo y cómo usarlo dentro de una aplicación web para asegurar la calidad y la solidez del código JavaScript de su aplicación.


Descargar

DescripciónNombretamaño
The source codesimple.zip3.35MB

Recursos

Aprender

Obtener los productos y tecnologías

Comentar

  • Conéctese con otros usuarios de developerWorks mediante la comunidad developerWorks mientras explora los blogs, foros, grupos y wikis relacionados con el desarrollador.
  • Ayude a desarrollar el grupoReal world open source en la comunidad de developerWorks.

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=tecnologia Java
ArticleID=795377
ArticleTitle=Pruebas eficientes de unidad de JavaScript
publish-date=02242012