TDD y TypeScript

0
5945

En este tutorial vamos a ver cómo configurar Karma que es actualmente el test runner más conocido para poder ejecutar toda la batería de tests con Jasmine cada vez que realicemos un cambio en nuestro código TypeScript lo que nos facilitará el trabajo con TDD.

0. Índice de contenidos

1. Entorno

Este tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil Mac Book Pro 15″ (2,3 Ghz Intel Core i7, 16 GB DDR3)
  • Sistema Operativo: Mac OS X El Capitan
  • NVM v0.29.0
  • NodeJS v0.12.7
  • npm 3.3.12
  • Atom 1.2.4

2. Introducción

En este tutorial vamos a ver cómo configurar Karma que es actualmente el test runner más conocido para poder ejecutar toda la batería de tests con Jasmine cada vez que realicemos un cambio en nuestro código TypeScript lo que nos facilitará el trabajo con TDD.

Vamos a configurar el proyecto que ya iniciamos en este tutorial anterior donde montamos un entorno TypeScript desde cero.

Como actualmente Karma no tiene un buen soporte para TypeScript, vamos a aprovechar los ficheros que genera Atom en la carpeta “build” para ejecutar los tests con los ficheros ya transpilados y veremos como instalar el fichero de definición de Jasmine, que nos permitirá implementar los tests con TypeScript.

Por último, configuraremos el proyecto para obtener informes de cobertura con Istanbul haciendo que el informe muestre el código TypeScript asociado para facilitar su claridad.

¡Vamos al lío!

3. Instalación y configuración de Karma

Lo primero que haremos será instalar karma como dependencia local y de desarrollo de nuestro proyecto. Ejecutamos esta sentencia en el proyecto raíz.

> npm install karma --save-dev

Lo que modificará nuestro fichero “package.json” para incluir la dependencia “karma” en la sección de “devDependencies”.

Seguidamente ejecutamos el comando “init” de karma que nos va a generar un fichero base llamado karma.conf.js y que iremos modificando en base a nuestras necesidades.

>  ./node_modules/karma/bin/karma init

El proceso de inicialización nos hará una serie de preguntas, fijaos cómo en función de lo que contestamos se va actualizando el fichero package.json con la dependencia necesaria.

  • Which testing framework do you want to use ? Jasmine
    Nos pregunta por el framework a utilizar, como hemos dicho vamos a optar por Jasmine, que quizá sea el framework de testing más conocido junto con Mocha.
  • Do you want to use Require.js ? No
    Require es una librería de carga de módulos, decimos que no ya que vamos a seguir aprovechando las ventajas de carga transparente y dinámica de JSPM.
  • Do you want to capture any browsers automatically ? Chrome (PhantomJS actualmente da problemas)
    Nos pregunta en qué navegadores queremos ejecutar los tests. En principio solo vamos a seleccionar Chrome, lo ideal sería hacerlo con PhantomJS para que no abriera ninguna ventana, pero a día de hoy da problemas.
  • What is the location of your source and test files ? Por defecto
    Nos pregunta por la localización de los ficheros que queremos que se carguen en el navegador. La dejamos por defecto porque JSPM tiene su propia forma de cargar estos ficheros.
  • Should any of the files included by the previous patterns be excluded ? Por defecto
    Ahora nos pregunta si queremos excluir algún fichero. En principio le decimos que no.
  • Do you want Karma to watch all the files and run the tests on change ? Yes
    Decimos que si ya que esto hace que cualquier cambio en los tests o en el código provoque el relanzamiento de los tests, con lo que es fácil ver si algún cambio ha producido fallo en algún tests.
  • Este es el fichero base que genera y al que vamos a hacer una serie de modificaciones para adaptarlo a la configuración de nuestro proyecto, dejándolo como se muestra a continuación:

    module.exports = function(config) {
      config.set({
        baseURL: '/',
        frameworks: ['jasmine', 'jspm'],
        jspm: {
          config: 'config.js',
          loadFiles: [
            'build/test/**/*Test.js'
          ],
          serveFiles: [
            'build/app/**/*.js'
          ]
        },
        exclude: [
        ],
        preprocessors: {
        },
        reporters: ['progress'],
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: true,
        browsers: ['Chrome'],
        singleRun: false
      })
    }
    

    La primera modificación es sustituir la propiedad “basePath” por “baseURL”, estableciendo el path a la raíz.

    Como nosotros vamos a aprovechar las ventajas de JSPM para la carga de librerías, en la sección de frameworks tenemos que añadir ‘jspm’.

    Al utilizar el framework jspm con karma la sección “files” se define dentro de la nueva sección “jspm”. Se compone de tres secciones: en la primera se indica el path del fichero config.js, en la segunda los ficheros de código de test que queremos tener en cuenta y cuyos nombre de fichero tienen que acabar con el sufijo Test, y por último los archivos de código de producción que tiene que cargar.

    Esta configuración requiere que nuestro proyecto tenga la dependencia “karma-jspm”, la instalamos como dependencia solo de desarrollo

    > npm install karma-jspm --save-dev
    

    4. Creación de un test de comprobación

    Ahora es el momento de crear nuestro test de comprobación, para ello y como vamos a utilizar TypeScript, tenemos antes que instalar el fichero de definición de TypeScript de Jasmine.

    > tsd install jasmine --save
    

    La opción –save hará que la definición se añada al fichero tsd.json y que se actualice el fichero tsd.d.ts con la referencia a esta dependencia.

    Hecho esto, creamos el fichero checkTest.ts dentro de la carpeta “test”. Lo primero es hacer referencia al fichero tsd.d.ts que contiene la definición de la librería Jasmine utilizada para la implementación.

    Después vamos a implementar un test suite al que llamaremos “Check configuration” con un test case simple que compruebe que true es true, esto lo hacemos para ver que la configuración de karma es correcta. Fijaos en la utilización de las “arrow functions” que son una de las novedades de ES6.

    /// <reference path="../typings/tsd.d.ts"/>
    describe('Check configuration', () => {
      it ('is true', () => {
        expect(true).toBe(true);
      })
    })
    

    Ahora ejecutamos la herramienta karma en el local de nuestro proyecto con el comando “start”.

    > ./node_modules/karma/bin/karma start
    

    Como podéis sería un rollo tener que escribir todo esto cada vez que ejecutamos los tests, por lo que vamos a hacer uso de la sección scripts del fichero package.json, estableciendo esta cadena para la propiedad “test”

    "scripts": {
        "test": "./node_modules/karma/bin/karma start"
      },
    

    De forma que ahora para ejecutar los tests solo tengamos que escribir:

    > npm test
    

    Si la configuración es correcta, tenemos que ver que se abre una ventana de Chrome, la cual podemos minimizar, nunca cerrar, y que en el terminal no se produce ningún tipo de error, advirtiendo que un test ha sido pasado con éxito.

    NOTA: Si en este punto se produce un error parecido a este:

    31 08 2015 15:11:25.894:WARN [web-server]: 404: /build/test/checkTest.js
    Chrome 44.0.2403 (Mac OS X 10.10.5) ERROR: 'Potentially unhandled rejection [5] Error: XHR error (404 Not Found) loading http://localhost:9876/build/test/checkTest.js
    

    Es bastante probable que se deba a que la propiedad baseURL del fichero config.js tiene un valor distinto de cadena vacía.

    Ahora podemos modificar el test para que falle y veremos como la consola nos informa del error al volver a ejecutar los tests o manteniendo la variable singleRun del fichero karma.conf.js a false.

    5. Configurar para obtener la cobertura de los tests

    Ahora vamos a incluir la funcionalidad de cobertura a nuestro proyecto, para ello vamos a añadir un nuevo reporter a nuestro fichero karma.conf.js y en la sección de preprocessor le vamos a indicar que utilice “coverage” con los ficheros de producción de nuestro proyecto.

    reporters: ['progress', 'coverage'],
    preprocessors: {
       'build/app/**/*.js': ['coverage']
    },
    

    Lo que implica añadir la dependencia “karma-coverage”:

    > npm install karma-coverage --save-dev
    

    Ahora si queremos mostrar la cobertura en la consola para ver rápidamente el resultado, tenemos que modificar el fichero karma.conf.js para especificar que queremos dos tipos de reporters, uno por consola y otro de tipo HTML en la carpeta “coverage” de nuestro proyecto.

    coverageReporter: {
          reporters: [
            {
              type: 'text-summary'
            },
            {
              type: 'html',
              dir: 'coverage/'
            }
          ]
        },
    

    En esta fase de configuración no nos interesa que karma se quede corriendo así que vamos a establecer la propiedad singleRun a true, para que la ventana del navegador se cierre automáticamente cuando termine de correr los tests, y volvemos a lanzar los tests.

    > npm test
    

    Tenemos que ver que por consola se muestra el índice de cobertura pero indicando 0%. Esto es debido a que nuestro test realmente no prueba nada de código real

    Para hacer que el test pruebe código real tenemos que modificarlo para que haga uso al menos de la clase Main. Primero importamos la clase con la sintaxis que ya conocemos y simplemente hacemos que en el caso de prueba se realice la instanciación de la clase, recordad que seguimos en fase de configuración.

    /// <reference path="../typings/tsd.d.ts"/>
    import {Main} from '../app/bootstrap';
    
    describe('Check configuration', () => {
      it('is true', () => {
        var m = new Main('Desde el test');
      })
    })
    

    Volvemos a lanzar los test y por consola tenemos que ver que efectivamente el grado de cobertura ha aumentado e incluso podemos ver los distintos logs que se están ejecutando.

    Si ahora vamos a ver los reportes generados en la carpeta “coverage” veremos que son correctos pero que el código hace referencia al fichero transpilado y no a nuestro fichero TypeScript implementado.

    Para resolver esto, tenemos lo que se conoce como “sourcemap” que son ficheros que guardan la relación entre el fichero real y el transpilado. Para poder hacer uso de ellos, primero le tenemos que decir a Atom que los genere, esto simplemente es añadir la propiedad “sourceMap” a true dentro del fichero “tsconfig.json”, justo debajo de donde añadimos la propiedad “outDir” en el anterior tutorial.

    Para asegurarnos que Atom toma esta nueva configuración,lo reiniciamos, ejecutando en el menú “View → Reload”. Borramos la carpeta “build” y volvemos a generar los ficheros pulsando en F6, tenemos que ver cómo se generan los ficheros .map asociados.

    Ahora modificamos el fichero de configuración de karma para indicar en la sección “preprocessor” que queremos incluir el preprocesado de “sourcemap”.

    preprocessors: {
          'build/app/**/*.js': ['sourcemap','coverage']
        },
    

    Esto implica añadir la dependencia “karma-sourcemap-loader”.

    > npm install --save-dev karma-sourcemap-loader
    

    Y dado que esta parte todavía está en una fase un poco experimental, tenemos que actualizar las librería de istanbul y karma-coverage de forma exacta a como se muestra a continuación:

    > npm install --save-dev istanbul@gotwarlost/istanbul#source-map
    > npm install --save-dev karma-coverage@douglasduteil/karma-coverage#next
    

    Ahora borramos la carpeta “coverage” para asegurarnos que se actualiza correctamente y volvemos a lanzar los tests, viendo que por pantalla antes del informe de cobertura se muestra: “Post-processing using source maps” y si vamos al informe de HTML vemos que ahora sí se muestra el código en TypeScript.

    6. Conclusiones

    Son muchos pequeños pasos los que hay que dar para tener correctamente configurado el proyecto y poder empezar a programar la solución que toque. Es estos tutoriales os hemos querido enseñar como hacerlo desde cero, ahora es una buena idea crear un generador de Yeoman o buscar uno que ya exista y que te proporcione toda esta configuración automáticamente.

    Cualquier duda o sugerencia en la zona de comentarios.

    Saludos.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad