my github
english | español
Waldo Urribarri HOME PROJECTS ABOUT ME


Probando aplicaciones Backbone.js-Underscore-RequireJS usando Jasmine

jasmine logo

Probar el software es importante, mientras antes mejor. Si estás trabajando en una aplicación pequeña o grande, podrías introducir bugs involuntariamente mientras haces modificaciones, y tener tests (unitarios o funcionales) te ayudarán a encontrarlos incluso al momento de la construcción, o por lo menos antes de ir a fase de QA. Esta vez te mostraré mi forma para implementar pruebas en mi aplicación web ES5 que está estructurada usando Backbone.js, Underscore, y RequireJS, usando una buena herramienta llamada Jasmine.

No cubriré cómo crear los tests ya que hay bastante documencación sobre el tema para eso, sino cómo configurar todo para comenzar a usarlos. Tenía un viejo proyecto sin pruebas implementadas y quise intentar agregarlas.

Mi proyecto está estructurado de la siguiente forma:

/
|__ js/
|______ models/
|______ views/
|______ [other .js files]
|__ node_modules/
|__ test/
|______ main.js
|______ SpecRunner.html
|__ index.html
|__ [everthing else]

Así que, lo principal es agregar el paquete jasmine en el package.json. Solo entra en la carpeta principal del proyecto y:

npm install –save-dev jasmine

Nota: Si te sale el error “Uncaught TypeError: Cannot read property 'spies' of undefined thrown”, podrías revertir a la versión 2.4.1. Usa npm install –save-dev [email protected] en este caso.

Una vez que tengas el paquete, es momento de configurar nuestro SpecRunner. Personalmente, prefiero incluirlo en una carpeta llamada /test. Hay varias plantillas para crearlo, pero te mostraré la que usé:

<!DOCTYPE html>
  <html>
    <head>
      <meta charset="utf-8" />
      <title>Jasmine Spec Runner</title>
      <link rel="shortcut icon" type="image/png" href="../node_modules/jasmine-core/images/jasmine_favicon.png" />
      <link rel="stylesheet" href="../node_modules/jasmine-core/lib/jasmine-core/jasmine.css" />
      <script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine.js">
      </script>
      <script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js">
      </script>
      <script src="../node_modules/jasmine-core/lib/jasmine-core/boot.js">
      </script>
      <script type="text/javascript" src="../node_modules/requirejs/require.js" data-main="main">
      </script>
      <script>
            function includeBody() {
                xhttp = new XMLHttpRequest();
                xhttp.onreadystatechange = function() {
                    if (this.readyState == 4) {
                        if (this.status == 200 || this.status == 0) {
                            var idx1 = this.responseText.indexOf('<body');
                            var idx2 = this.responseText.indexOf('</body>');
                            var body = this.responseText.substring(idx1, idx2 + 7);
                            var container = document.createElement("div");
                            container.style.display = 'none';
                            container.innerHTML = body;
                            document.getElementsByTagName('body')[0].appendChild(container);
                        }
                    }
                } 
                xhttp.open("GET", '../index.html', true);
                xhttp.send();
            }
        </script>
  </head>
  <body>
    <script>includeBody();</script>
  </body>
</html>

Link a código fuente: SpecRunner.html

Nota: La función includeBody() es una personalización para mi proyecto. Te cuento de eso en un momento.

La primera parte del SpecRunner solo carga el core de jasmine, y luego cargo require y mi configuración principal.

require.config({
  baseUrl: '../', // to set the default folder
  paths: {
    'jasmine': ['./node_modules/jasmine-core/lib/jasmine-core/jasmine'],
    'jasmine-html': ['./node_modules/jasmine-core/lib/jasmine-core/jasmine-html'],
    'jasmine-boot': ['./node_modules/jasmine-core/lib/jasmine-core/boot'],

    //add all modules that are required inside your models/views/etc
  },
  shim: {
    'jasmine-html': {
      deps: ['jasmine']
    },
    'jasmine-boot': {
      deps: ['jasmine', 'jasmine-html']
    }
  }
});

require(['jasmine-boot'], function () {
  require([
    'js/models/EntryAreaModel.spec'
    // require your specs here
    ], function(){
      window.onload();
  })
});

Como puedes ver, en la sección 'paths', debes agregar los módulos jasmine y también todos los otros módulos que puedan ser requeridos por ti. Luego en el require inferior, agregar todos los specs que serán probados.

Link a código fuente: main.js

Luego de esto, tus módulos Backbone estarán disponibles para ser requeridos en tus pruebas. Ejemplo:

define(['js/models/EntryAreaModel'], function(EntryAreaModel) {
  describe('EntryAreaModel tests', function() {
    describe('new models', function() {
      var model = new EntryAreaModel();
      it('should have indentType property', function() {
        expect(model.get('indentType')).toBeDefined();
      });
      it('should have indents property', function() {
        expect(model.get('indents')).toBeDefined();
      });
      it('without params are valid', function() {
        expect(model.isValid()).toBe(true);
      });
    });
  });
});

Link a código fuente: EntryAreaModel.spec.js

Ahora, puedes abrir tu SpecRunner.html en un explorador web para correr las pruebas y ver cualquier error.

Jasmine Spec Runner

Sobre la función includeBody()

Como las Views de Backbone necesitan un DOM para manipular y también los templates de Underscore para crear elementos, tuve que agregar todo el body de mi index.html para hacer que funcionaran mis tests de Views. La mejor forma que conseguí fue incluir el body dentro de mi SpecRunner. :) No estoy seguro que esta sea la mejor forma, pero funciona.

Espero que esto te ayude con tus tests ;)