Ecosistema de desarrollo de aplicaciones JavaScript con herramientas Microsoft

 

Siguiendo con nuestra serie de posts invitados, hoy os traemos uno muy interesante de Vicenç García Altés, Development Advisor en Plain Concepts. Lo encontraréis de gran utilidad.

Un saludo,

El equipo de MSDN.

 

 

En los últimos años hemos vivido un incremento espectacular en el uso de JavaScript en aplicaciones web, llegando a ser comunes las Single Page Applications, en las que se minimizan las peticiones de páginas al servidor web. Con la aparición de nodeJs (recientemente incorporado a Windows Azure) y la inminente salida al mercado de Windows 8, JavaScript se ha convertido en un lenguaje de primer nivel. Es por ello que tanto Microsoft como desarrolladores independientes están haciendo un esfuerzo para brindarnos las mejores herramientas con las que desarrollar en este lenguaje de la manera más profesional posible.

En este artículo veremos un pequeño ejemplo de código JavaScript que utilizaremos como campo de pruebas para montar un ecosistema de trabajo, tanto en local como en nuestro querido Team Foundation Server.

 

El código

El código del ejemplo lo desarrollaremos utilizando Test Driven Development. Para crear los tests utilizaremos una magnífica librería llamada Jasmine que podéis encontrar aquí. En nuestro caso utilizaremos la versión 1.2.

Jasmine es un framework open source de testeo, concretamente de Behavior Driven Development, muy sencillo de usar y que tiene como ventaja que ya incorpora un sistema de dobles de pruebas que funciona muy bien y que cubre los casos más comunes.

Una vez descargado, deberemos montar una estructura de directorios parecida a esta en, por ejemplo, un proyecto web vacío.

clip_image001

Estructura inicial de la solución

Si nos fijamos en el archivo SpecRunner.html veremos que es la página web donde correrán los tests. Tenemos una sección donde tendremos que referenciar todos nuestros archivos de código fuente y otra sección donde tendremos que referenciar todos los archivos de test que queremos pasar.

clip_image002

Fichero SpecRunner.html sin tests

Si ahora navegamos hasta esta página veremos que por ahora es una página en blanco. A medida que vayamos incorporando tests a nuestra solución, es en esta página donde podríamos ver el resultado. Más adelante veremos cómo cambiar esta manera de visualizar los resultados de los tests para integrarlos en Visual Studio 2012.

 

El primer test

El código que vamos a desarrollar va a ser una pequeña gestión de un equipo de futbol antes de un partido. Este código nos permitirá crear el once inicial y los siete reservas con una serie de restricciones que iremos viendo.

Una de las características de los frameworks de BDD es que nos permiten modelar escenarios de una manera sencilla. Por ejemplo, vamos ahora a definir el escenario de hacer el equipo titular.

Para definir una suite de tests en Jasmine (lo que sería nuestro escenario) utilizamos la función describe. Dentro de esta función es donde iremos escribiendo nuestros tests con la ayuda de la función it. Mucho mejor si lo vemos con un ejemplo: vamos a hacer un nuevo fichero JavaScript dentro de la carpeta specs y vamos a escribir el siguiente código:

describe("in order to make a first team", function () {

    it("we have to be able to add a player", function () {

        var player = new JavaScriptSample.Entities.Player('Leo',

                                                          'Messi',

                                         JavaScriptSample.Enumerations.playerPosition.STRIKER,

                                                          24);

        var playingTeam = new JavaScriptSample.Domain.PlayingTeam();

        playingTeam.addPlayerToFirstTeam(player);

 

  expect(playingTeam.isFirstTeamComplete()).toBe(false);

    });

});

Como podemos ver estamos definiendo una suite que nos servirá para los tests encargados de la creación del equipo inicial y estamos escribiendo nuestro primer test, intentando añadir un jugador al once inicial de un equipo.

Añadimos la referencia a este script en el runner de Jasmine y refrescamos el explorador para ver los resultados.

clip_image004

Primer test fallido en Jasmine

Obviamente el test no pasa. Tenemos un montón de cosas sin definir. Empezamos añadiendo el fichero enumerations.js a la carpeta src y escribiendo el siguiente código:

var JavaScriptSample = JavaScriptSample || {};

JavaScriptSample.Enumerations = JavaScriptSample.Enumerations || {};

 

JavaScriptSample.Enumerations.playerPosition = {

    STRIKER: 'striker',

};

 

Pasamos a crear el archivo entities.js y a escribir el siguiente código:

var JavaScriptSample = JavaScriptSample || {};

JavaScriptSample.Entities = JavaScriptSample.Entities || {};

 

JavaScriptSample.Entities.Player = function (firstName, lastName, position, age) {

    var that = this;

    that.firstName = firstName;

    that.lastName = lastName;

    that.position = position;

    that.age = age;

};

Y finalmente vamos a crear el archivo domain.js y a escribir el siguiente código:

var JavaScriptSample = JavaScriptSample || {};

JavaScriptSample.Domain = JavaScriptSample.Domain || {};

 

JavaScriptSample.Domain.PlayingTeam = function () {

    var that = this;

    that.firstTeam = new Array();

 

    that.addPlayerToFirstTeam = function (player) {

        that.firstTeam.push(player);

    };

    that.isFirstTeamComplete = function () {

        return false;

    };

};

Actualizamos el fichero SpecRunner.html para tener en cuenta estos ficheros y ya podemos volver a lanzar los tests actualizando el navegador:

clip_image006

Primer test en verde en Jasmine.

Y ya los tenemos en verde. A parte, y es algo que parece una tontería pero es muy importante, podemos ver como el resultado se puede leer y entender sin problemas, con lo que rápidamente sabemos que está haciendo el código que estamos probando.

 

Ahora que ya tenemos un poco de código escrito, es bueno empezar a comprobar que dicho código está bien escrito. Para los que desarrollen con Visual Studio 2010, una extensión muy útil es jslint for Visual Studio 2010, que nos permite analizar nuestro código con JSLint o con JSHint, de una manera parecida a como lo hace StyleCop con nuestro código C#. Desgraciadamente, para Visual Studio 2012 esta extensión todavía no está disponible. Mientras esperamos a que salga, podemos utilizar otra extensión llamada Chirpy. Chirpy es una extensión que permite realizar muchas cosas: minimizar archivos js, manejar archivos coffescript, manejar archivos LESS, etc. Y también nos permite pasar JSHint en nuestros archivos JavaScript.

clip_image008

Chirpy.

Instalar la extensión es muy sencillo. Nos vamos a la página principal del proyecto y nos descargamos el .vsi de la versión 2.0.6 (Margogype Chirpy). Cerramos nuestros Visual Studio 2012 que tuviéramos arrancados y ejecutamos el instalador de la extensión. Atentos porque nos dará una advertencia de seguridad, pero no hay que temer por nada y podemos instalar la extensión sin problema.

clip_image009

Instalando Chirpy.

Le damos a Finish y ya lo tenemos instalado. Si ahora abrimos nuestro Visual Studio 2012 y nos vamos a TOOLS -> Options, veremos que tenemos una nueva opción del menú llamada Chirpy. Si nos vamos al submenú JSHint y activamos la opción “Run JS Hint”, cada vez que gravemos un archivo js, automáticamente nos analizará el código según las opciones que hayamos configurado en esta página. Como siempre, cuanto más restrictivos seamos, mejor será nuestro código.

clip_image011

Configuración de Chirpy.

Vamos entonces a probarlo. Hacemos cualquier cambio en el archivo domain.js y guardamos los cambios. Como veis tenemos un error:

clip_image013

Código JavaScript con un warning de JSHint.

Esto es debido a qué hemos definido un array de la siguiente manera:

that.firstTeam = new Array();

Cambiamos este código por el siguiente:

that.firstTeam = [];

Y ya tenemos el error solucionado.

 

Seguimos con los tests

Continuemos entonces añadiendo más tests a nuestros ficheros JavaScript. Por ejemplo, pongamos la condición que si añadimos más de 11 jugadores, nos salte una excepción:

 

it("when try to add twelfth player, exception is thrown", function () {

        var player = new JavaScriptSample.Entities.Player('Leo',

                                                          'Messi',

              JavaScriptSample.Enumerations.playerPosition.STRIKER,

                                                          30);

        var playingTeam = new JavaScriptSample.Domain.PlayingTeam();

 

        for (var i = 0; i < 11; i++) {

            playingTeam.addPlayerToFirstTeam(player);

        }

 

        expect(function () { playingTeam.addPlayerToFirstTeam(player); }).toThrow(new Error("TooManyPlayersException"));

    });

 

Vemos que si pasamos los tests, el nuevo test está en rojo:

clip_image015

Error al no lanzar una excepción.

Escribimos el código que hace pasar el test:

that.addPlayerToFirstTeam = function (player) {

        if (that.firstTeam.length === 11) {

            throw "TooManyPlayersException";

        }

        else {

            that.firstTeam.push(player);

        }

    };

Y ya tenemos el test en verde.

clip_image017

Test en verde al lanzar la excepción.

Ahora ya podemos refactorizar. Podríamos, por ejemplo, sacar la asignación a un método a parte y sacar el “ magic number ” 11 a una constante. Para fines didácticos, nos centraremos en el código de los tests. Si nos fijamos, vemos que al principio de cada test hay código repetido. Este código, lo podríamos sacar a una función que se ejecutara antes de la ejecución de cada test. Esto lo podemos hacer gracias a la función beforeEach de esta manera:

describe("in order to make a first team", function () {

    var player, playingTeam;

 

    beforeEach(function () {

        player = new JavaScriptSample.Entities.Player('Leo',

                                                          'Messi',

                                   JavaScriptSample.Enumerations.playerPosition.STRIKER,

                                                          30);

        playingTeam = new JavaScriptSample.Domain.PlayingTeam();

    });

    …

});

Haciendo esta pequeña refactorización, el código de nuestros tests queda mucho más limpio y sin duplicidades, al igual que hacemos con el método TestInitialize en MSTest.

 

Los espías de Jasmine

Imaginemos ahora que queremos testear que cuando llamamos a la función saveTeam, se está llamando por Ajax a una función de nuestro controlador, servicio REST o lo que sea, pero sin tener ni tan solo codificado dichos componentes. O queremos simular que la llamada a un tercer componente devuelve un determinado valor.  Esto ya lo hacíamos habitualmente en .Net y por lo tanto ya sabemos que tenemos que utilizar dobles de pruebas. Tenéis una explicación de lo que son los dobles de pruebas y sus diferentes variantes en este magnífico artículo de Martin Fowler.

Esta funcionalidad en Jasmine se agrupa bajo la denominación de spy. Con un spy podremos saber si se ha llamado a una función, con qué parámetros, cuantas veces, simular su valor de retorno, etc.

Empecemos escribiendo nuestro test:

it("when saving a playing team, ajax call is made", function () {

        spyOn($, "ajax");

        playingTeam.saveTeam();

        expect($.ajax).toHaveBeenCalled();

    });

Como podemos ver, estamos diciéndole a Jasmine que, cuando yo esté llamando a la función saveTeam, espero que se llame en algún momento a la función Ajax del objeto $ (jquery).

Ahora ya podemos escribir la función que hace pasar el test:

that.saveTeam = function () {

        $.ajax({

            url: "/Team/SaveFirstTeam",

            data: {

                team: JSON.stringify(that.firstTeam)

        },

            type: 'POST',

            dataType: 'json'

        });

    };

También podríamos simular que la llamada Ajax se realiza correctamente. Para hacerlo necesitaríamos un test como este:

it("when saving a playing team success, isDirty is set to false", function () {

        spyOn($, "ajax").andCallFake(function (options) {

            options.success();

        });

        playingTeam.saveTeam();

        expect(playingTeam.isDirty).toBe(false);

    });

Y añadiendo el manejador success en la llamada Ajax, haríamos pasar el test a verde:

that.saveTeam = function () {

        $.ajax({

            url: "/Team/SaveFirstTeam",

            data: {

                team: JSON.stringify(that.firstTeam)

            },

            type: 'POST',

            dataType: 'json',

            success: function () {

                that.isDirty = false;

            }

        });

    };

 

Chutzpah

Con el poco código que hemos hecho y los pocos tests que tenemos, ya los hemos ejecutado todos unas cuantas veces.  Cada vez que lo hemos hecho hemos tenido que ir al navegador y actualizar la página del SpecRunner. ¿No sería mucho mejor poder tener integrados los tests de Jasmine con VS2012? Existe una extensión de Visual Studio llamada Chutzpah que nos realiza esta función,  aprovechándose de la nueva arquitectura de Visual Studio  que facilita la integración de otros frameworks de testeo unitario.

Para instalar la extensión debemos irnos al menú TOOLS -> Extensions and updates -> Online -> Visual Studio Gallery -> Tools -> Testing e instalar la extensión:

clip_image019

Instalando la extensión Chutzpah.

Una vez reiniciado Visual Studio, nos vamos a la ventana de Test Explorer y hacemos correr todos los tests, veremos que nos aparecen los tests que tengamos de Jasmine en nuestra solución (y también de QUnit con su resultado).

clip_image021

TestExplorer de Visual Studio 2012 con tests en JavaScript.

Lo único que hemos perdido respecto a la versión HTML es la jerarquía de los tests, pero a cambio en el nombre de cada test nos ha añadido el nombre del describe “padre”. Es un pequeño precio a pagar por el hecho de tenerlo integrado en Visual Studio.

 

¡En mi máquina funciona!

¿Quién no ha dicho alguna vez esta frase? Para evitar repetirla más veces, disponer de un entorno de integración continua se antoja imprescindible. Trabajando con tecnologías Microsoft, Team Foundation Server 2012 es nuestra mejor opción.

Hasta ahora integrar un framework de test que no fuera MSTest en una build era un trabajo de artesanía. Al igual que pasa en Visual Studio 2012, en Team Foundation Server 2012 también se ha facilitado mucho esta integración. Veamos cómo podemos hacerlo en un TFS Service.

Lo primero que tendremos que hacer es bajarnos la extensión de Chutzpah a nuestro disco duro, cambiar la extensión .vsix por .zip y descomprimir dicho zip. Una vez hecho esto hacemos una nueva carpeta en nuestro explorador de código fuente y subimos todo el contenido a dicha carpeta:

clip_image023

Control de código fuente de Team Foundation 2012

Ahora le tenemos que indicar a nuestro Build Controller que utilice esta carpeta como carpeta contenedor de los custom assemblies. Para hacerlo nos vamos a la sección de builds del Team Explorer, clicamos sobre Actions y seleccionamos la acción Manage Build Controllers.

clip_image025

Acceso a la gestión del Build Controller.

Una vez estamos en las propiedades del Build Controller, establecemos dicha carpeta en el apartado “version control path to custom assemblies”:

clip_image027

Propiedades del Build Controller.

A la hora de crear nuestra build, lo único especial que tenemos que tener en cuenta es que en el apartado de proceso, en la sección de automated tests hay que definirle un nuevo conjunto de tests a ejecutar. 

clip_image029

Configuración de los tests en la build.

Le tenemos que indicar que también queremos que se pasen los tests de los archivos que tengan la extensión js.

clip_image031

Añadir los tests de JavaScript a la build.

Si ahora pasamos la build, en la sección de tests, veremos que se han pasado los tests de JavaScript que habíamos definido:

clip_image033

Resumen de la build.

 

Conclusiones

Hemos visto cómo podemos desarrollar utilizando tests unitarios en JavaScript. Hemos visto cómo podemos pasar un analizador de código estático a nuestro código JavaScript. Y finalmente hemos visto cómo integrar los tests unitarios tanto en Visual Studio 2012 como en Team Foundation Server 2012 consiguiendo así un ecosistema completo de desarrollo en JavaScript.

 

Autor

Vicenç García Altés es Development Advisor en Plain Concepts.

Twitter: @vgaltes

Blog: https://geeks.ms/blogs/devnettips

Linkedin: https://www.linkedin.com/in/vgaltes