Pruebas de Ember.js

Cuando comencé a jugar con Ember.js hace casi un año, la historia de las comprobaciones dejaba algo que desear. Usted podría probar un objeto sin problemas, pero una prueba unitaria es solo una forma de obtener retroalimentación cuando está creando un producto de software. Además de las pruebas unitarias, quería una forma de verificar la integración de múltiples componentes. Así que, como la mayoría de las personas que prueban aplicaciones ricas de JavaScript, busqué a la madre de todas las herramientas de prueba, Selenium..

Ahora, antes de golpearlo, sin una introducción adecuada, vale la pena mencionar que Selenium es una excelente manera de verificar que toda su aplicación web funciona con una base de datos completa de producción y todas sus dependencias de producción, etc. Y desde una perspectiva de control de calidad, esta herramienta puede ser un gran recurso para los equipos que necesitan pruebas de aceptación de IU de extremo a extremo.

Pero con el tiempo, un conjunto de pruebas aparentemente pequeño basado en Selenium puede comenzar a arrastrar la velocidad de su equipo a un ritmo de caracoles. Una forma fácil de reducir este dolor es evitar la creación de una aplicación grande en primer lugar. Si construyes un puñado de aplicaciones web más pequeñas en su lugar, podría ayudarte a mantenerte a flote por un poco más de tiempo porque ninguna compilación individual aplastará al equipo, a medida que crezcas..

Pero incluso en un proyecto pequeño, el problema real con Selenium es que no es parte del proceso de desarrollo basado en pruebas. Cuando estoy haciendo rojo / verde / refactor no tengo tiempo para comentarios lentos en ninguna forma. Necesitaba una forma de escribir pruebas de unidad e integración que me proporcionaran una retroalimentación rápida para ayudarme a configurar el software que estaba escribiendo de una manera más iterativa. Si está utilizando una versión de Ember.js> = RC3, está de suerte porque escribir una prueba de unidad o integración es un paso por la parte.


Instalación del corredor de prueba

Ahora que podemos escribir pruebas de JavaScript para nuestra aplicación, ¿cómo las ejecutamos? La mayoría de los desarrolladores comienzan a usar el navegador directamente, pero como quería algo que pudiera ejecutar desde la línea de comandos en un entorno de CI con un rico ecosistema lleno de complementos, miré al Karma.

Lo que me gustó de Karma es que solo quiere ser tu corredor de prueba. No importa qué marco de prueba de JavaScript utilice o qué marco de MVC del lado del cliente utilice. Es sencillo comenzar y escribir pruebas que se ejecutan en la aplicación de producción Ember.js es solo unas pocas líneas de configuración.

Pero antes de que podamos configurar Karma, necesitamos instalarlo usando npm. Recomiendo instalarlo localmente para que pueda mantener sus módulos npm aislados por proyecto. Para hacer esto, agrega un archivo llamado paquete.json'a la raíz de su proyecto que se parece en algo a la siguiente.

 "dependencias": "karma-qunit": "*", "karma": "0.10.2"

Este ejemplo requerirá Karma y un complemento para QUnit. Después de guardar el paquete.json archivo de arriba, vuelva a la línea de comandos y escriba npm instalar para bajar los módulos de Nodo requeridos.

Una vez completada la instalación de npm, ahora verá una nueva carpeta con el nombre nodo_módulos En la raíz de tu proyecto. Esta carpeta contiene todo el código JavaScript que acabamos de bajar con npm, incluidos Karma y el complemento QUnit. Si profundizas aún más para nódulos de nodo / karma / bin / Verás el ejecutable Karma. Usaremos esto para configurar el corredor de prueba, ejecutar pruebas desde la línea de comandos, etc..


Configurar el corredor de prueba

Luego necesitamos configurar el karma para que sepa cómo ejecutar las pruebas QUnit. Tipo karma init Desde la raíz del proyecto. Se le pedirá una lista de preguntas. El primero preguntará qué marco de prueba desea utilizar, pulse Lengüeta hasta que veas qunit, luego pulsa Entrar. Siguiente respuesta no a la pregunta Require.js, ya que no la usaremos para esta aplicación de muestra. Lengüeta hasta que veas PhantomJS para la tercera pregunta y tendrás que golpear Entrar El doble que permite múltiples opciones aquí. En cuanto al resto, simplemente déjalos en su opción por defecto..

Cuando hayas terminado, deberías ver que Karma ha generado un archivo de configuración llamado karma.conf.js En la raíz o en tu proyecto. Si desea leer más sobre las diversas opciones que admite Karma, puede encontrar los comentarios útiles. Por el bien de este ejemplo, tengo una versión simplificada del archivo de configuración para que sea fácil para los principiantes.

Si desea seguir adelante, elimine el archivo de configuración generado y reemplácelo con este.

 module.exports = function (karma) karma.set (basePath: 'js', archivos: ["vendor / jquery / jquery.min.js", "vendor / handlebars / handlebars.js", "vendor / ember / ember.js "," vendor / jquery-mockjax / jquery.mockjax.js "," app.js "," tests / *. js "], logLevel: karma.LOG_ERROR, navegadores: ['PhantomJS'], singleRun: true, autoWatch: false, frameworks: ["qunit"]); ;

Esto debería ser bastante similar a lo que generó Karma anteriormente, solo eliminé todos los comentarios y eliminé algunas opciones que no nos interesan en este momento. Para poder escribir la primera prueba de unidad, tuve que contarle un poco más a Karma sobre la estructura del proyecto..

En la parte superior del archivo de configuración, verá que he establecido el basePath a js porque todos los recursos de JavaScript viven en esta carpeta en el proyecto. A continuación, le dije a Karma dónde puede encontrar los archivos JavaScript necesarios para probar nuestra sencilla aplicación. Esto incluye jQuery, Handlebars, Ember.js y el app.js archivo en sí.


Escribiendo la primera prueba de unidad

Ahora podemos agregar el primer archivo de prueba de unidad al proyecto. Primero haz una nueva carpeta llamada pruebas y anidarla bajo el js carpeta. Agrega un archivo en este nuevo directorio llamado unit_tests.js que se ve algo como esto.

 prueba ('hola mundo', función () igual (1, 1, ""););

Esta prueba no está haciendo nada valioso todavía, pero nos ayudará a verificar que tenemos todo conectado con Karma para ejecutarlo correctamente. Aviso en el Karma archivos sección, ya hemos añadido la js / tests directorio. De esta manera, Karma obtendrá todos los archivos JavaScript que usamos para probar nuestra aplicación, en el futuro..

Ahora que tenemos Karma configurado correctamente, ejecute las pruebas qunit desde la línea de comandos usando ./ node_modules / karma / bin / karma start.

Si tiene todo configurado correctamente, debería ver Karma ejecutar una prueba y tener éxito. Para verificar que ejecutó la prueba que acabamos de escribir, vaya a hacer que falle modificando la instrucción igual. Por ejemplo, podrías hacer lo siguiente:

 prueba ('hola mundo', función () igual (1, 2, "boom"););

Si puede fallar esto y hacerlo pasar de nuevo, es hora de escribir una prueba con un poco más de propósito.


La aplicación de muestra

Pero antes de comenzar, analicemos la aplicación de ejemplo utilizada en esta publicación. En la siguiente captura de pantalla, ves que tenemos una grilla de usuarios muy simple. En la tabla HTML, se muestra a cada usuario por su nombre junto con un botón para eliminar a ese usuario. En la parte superior de la aplicación, verá una entrada para el nombre, el apellido y finalmente un botón que agregará otro usuario a la tabla cuando se haga clic..

https://dl.dropboxusercontent.com/u/716525/content/images/2013/pre-tuts.png

La aplicación de ejemplo tiene tres problemas. Primero, queremos mostrar el nombre y apellido del usuario, no solo el nombre. A continuación, al hacer clic en un botón de eliminar, en realidad no eliminará al usuario. Y finalmente, cuando agrega un nombre, un apellido y hace clic en agregar, no pondrá a otro usuario en la tabla.

En la superficie, el cambio de nombre completo parece ser el más simple. También resultó ser un gran ejemplo que muestra cuándo se debe escribir una prueba de unidad, una prueba de integración o ambas. En este ejemplo, la forma más rápida de obtener retroalimentación es escribir una prueba unitaria simple que afirme que el modelo tiene una propiedad calculada. nombre completo.


Unidad de prueba de la propiedad calculada

La prueba unitaria de un objeto de brasa es fácil, simplemente cree una nueva instancia del objeto y solicite la nombre completo valor.

 test (la propiedad 'fullName devuelve el primero y el último', function () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ; igual (resultado, 'toran billups', "fullName was" + result););

Siguiente si vuelves a la línea de comandos y ejecutas ./ node_modules / karma / bin / karma start, debería mostrar una prueba fallida con un mensaje útil que describa nombre completo como indefinido actualmente. Para arreglar esto, necesitamos abrir el app.js presentar y agregar una propiedad computada al modelo que devuelve una cadena de los valores de nombre y apellido combinados.

 App.Person = Ember.Object.extend (firstName: ", lastName:", fullName: function () var firstName = this.get ('firstName'); var lastName = this.get ('lastName'); return nombre + "+ apellido; .propiedad ());

Si vuelves a la línea de comandos y ejecutas ./ node_modules / karma / bin / karma start Ahora debería ver una prueba de unidad de paso. Puede ampliar este ejemplo escribiendo algunas otras pruebas de unidad para mostrar que la propiedad calculada debe cambiar cuando se actualice el nombre o el apellido en el modelo..

 test (la propiedad 'fullName devuelve primero y último', function () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ; igual (resultado, 'toran billups', "fullName was" + result);); prueba ('la propiedad fullName se actualiza cuando se cambia el nombre', función () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ); igual (resultado, 'toran billups', "fullName era" + resultado); person.set ('firstName', 'wat'); result = person.get ('fullName'); igual (resultado, 'wat billups ', "fullName was" + resultado);); prueba ('la propiedad fullName se actualiza cuando se cambia el apellido', función () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ); igual (resultado, 'toran billups', "fullName era" + resultado); person.set ('lastName', 'tbozz'); result = person.get ('fullName'); igual (resultado, 'toran tbozz ', "fullName was" + resultado););

Si agrega estas dos pruebas adicionales y ejecuta las tres desde la línea de comandos, debe tener dos errores. Para que las tres pruebas pasen, modifique la propiedad calculada para escuchar los cambios tanto en el nombre como en el apellido. Ahora si corres ./ node_modules / karma / bin / karma start Desde la línea de comando, deberías tener tres pruebas aprobatorias..

 App.Person = Ember.Object.extend (firstName: ", lastName:", fullName: function () var firstName = this.get ('firstName'); var lastName = this.get ('lastName'); return nombre + "+ apellido; .propiedad ('nombre', 'apellido'));

Agregue el preprocesador Karma-Ember y configúrelo

Ahora que tenemos una propiedad calculada en el modelo, debemos mirar la plantilla en sí misma porque actualmente no usamos la nueva nombre completo propiedad. En el pasado, necesitarías conectar todo por ti mismo o usar Selenium para verificar que la plantilla se procesa correctamente. Pero con la prueba de brasas, ahora puede realizar una prueba de integración agregando algunas líneas de JavaScript y un complemento para Karma.

Primero abre el paquete.json Archivo y agregue la dependencia karma-ember-preprocesador. Después de actualizar el paquete.json archivo, hacer npm instalar desde la línea de comandos para bajar esto.

 "dependencias": "karma-brasa-preprocesador": "*", "karma-qunit": "*", "karma": "0.10.2"

Ahora que tiene el preprocesador instalado, necesitamos que Karma esté al tanto de los archivos de plantilla. En el archivos sección de tu karma.conf.js archivo agregue lo siguiente para informarle a Karma sobre las plantillas de manillares.

 module.exports = function (karma) karma.set (basePath: 'js', archivos: ["vendor / jquery / jquery.min.js", "vendor / handlebars / handlebars.js", "vendor / ember / ember.js "," vendor / jquery-mockjax / jquery.mockjax.js "," app.js "," tests / *. js "," templates / *. handlebars "], logLevel: karma.LOG_ERROR, browsers: ['PhantomJS'], singleRun: true, autoWatch: false, frameworks: ["qunit"]); ;

Luego debemos decirle a Karma qué hacer con estos archivos de manillares, porque técnicamente queremos que cada plantilla se compile previamente antes de que se la entregue a PhantomJS. Agregue la configuración del preprocesador y apunte cualquier cosa con una extensión de archivo de *.bigote daliniano en el preprocesador de brasas. También debe agregar la configuración de complementos para registrar el preprocesador de brasas (junto con algunos otros que normalmente se incluyen con la configuración predeterminada de Karma).

 module.exports = function (karma) karma.set (basePath: 'js', archivos: ["vendor / jquery / jquery.min.js", "vendor / handlebars / handlebars.js", "vendor / ember / ember.js "," vendor / jquery-mockjax / jquery.mockjax.js "," app.js "," tests / *. js "," templates / *. handlebars "], logLevel: karma.LOG_ERROR, browsers: ['PhantomJS'], singleRun: true, autoWatch: false, frameworks: ["qunit"], plugins: ['karma-qunit', 'karma-chrome-launcher', 'karma-kember-preprocessor', 'karma- phantomjs-launcher '], preprocesadores: "** / *. handlebars":' ember '); ;

Prueba de integración de la plantilla enlazada a datos

Ahora que tenemos la configuración de configuración de Karma para las pruebas de integración, agregue un nuevo archivo llamado integration_tests.js bajo la pruebas carpeta. Dentro de esta carpeta, debemos agregar una prueba simple para demostrar que podemos hacer frente a toda la aplicación Ember.js sin error. Agregue una prueba de qunit simple para ver si podemos golpear el '/' enrutar y obtener el HTML básico devuelto. Para la prueba inicial, solo estamos afirmando que el mesa etiqueta existe en el HTML que se generó.

 prueba ('hola mundo', función () App.reset (); visita ("/"). luego (función () ok (existe ("tabla"));););

Note que estamos usando algunos ayudantes que están integrados en las pruebas de brasas como visitar y encontrar. los visitar helper es una manera amigable de brasas de decirle a la aplicación en qué estado estar durante la ejecución. Esta prueba comienza en el '/' ruta porque ahí es donde los modelos de People se unen a la plantilla y se genera nuestra tabla HTML. los encontrar helper es una forma rápida de buscar elementos en el DOM usando selectores de CSS como lo haría con jQuery para verificar algo sobre el marcado.

Antes de que podamos ejecutar esta prueba, necesitamos agregar un archivo de ayuda de prueba que inyectará a los asistentes de prueba y establecerá un elemento raíz genérico. Agregue el código a continuación, a un archivo llamado integration_test_helper.js en el mismo pruebas directorio. Esto asegurará que nuestra aplicación tenga los ayudantes de prueba en el momento de la ejecución..

 document.write ('
'); App.rootElement = '# ember-testing'; App.setupForTesting (); App.injectTestHelpers (); la función existe (selector) return !! find (selector) .length;

Ahora desde la línea de comandos debería poder ejecutar la prueba de integración anterior. Si obtuvo una prueba de aprobación, elimine la tabla de la plantilla de manillares para que falle (solo para ayudar a demostrar que Ember estaba generando el HTML usando esa plantilla).

Ahora que tenemos la configuración de las pruebas de integración, es hora de escribir la que afirma que mostramos a cada usuario nombre completo en lugar de su nombre de pila. Primero queremos afirmar que obtenemos dos filas, una para cada persona.

 test ('hello world', function () App.reset (); visit ("/"). then (function () var rows = find ("table tr"). length; igual (filas, 2, filas );););

Nota: la aplicación actualmente está devolviendo datos codificados para mantener todo simple en este momento. Si tienes curiosidad de por qué tenemos dos personas, aquí está el encontrar Método en el modelo:

 App.Person.reopenClass (people: [], find: function () var first = App.Person.create (firstName: 'x', lastName: 'y'); var last = App.Person.create (firstName: 'x', lastName: 'y'); this.people.pushObject (first); this.people.pushObject (last); devuelva this.people;);

Si realizamos las pruebas ahora, todavía deberíamos pasar todo porque se devuelven dos personas como es de esperar. A continuación, necesitamos obtener la celda de la tabla que muestra el nombre de la persona y afirmar que está usando la nombre completo propiedad en lugar de solo nombre de pila.

 test ('hello world', function () App.reset (); visit ("/"). then (function () var rows = find ("table tr"). length; igual (filas, 2, filas ); var fullName = find ("table tr: eq (0) td: eq (0)"). text (); igual (fullName, "xy", "la primera fila de la tabla tenía fullName:" + fullName); ););

Si ejecuta la prueba anterior, debería ver una prueba fallida porque todavía no hemos actualizado la plantilla para usar nombre completo. Ahora que tenemos una prueba que falla, actualice la plantilla para usar nombre completo y ejecuta las pruebas usando ./ node_modules / karma / bin / karma start. Ahora debería tener un conjunto de pruebas de unidad e integración.


¿Debo escribir pruebas de unidad o integración??

Si se está preguntando, "¿cuándo debo escribir una prueba unitaria frente a una prueba de integración?", La respuesta es simple: ¿qué será menos doloroso? Si escribir una prueba de unidad es más rápido y explica el problema mejor que una prueba de integración mucho más grande, entonces le digo que escriba la prueba de unidad. Si las pruebas unitarias parecen menos valiosas porque está haciendo un CRUD básico y el comportamiento real está en la interacción entre los componentes, le digo que escriba la prueba de integración. Debido a que las pruebas de integración escritas con las pruebas de brasas son increíblemente rápidas, son parte del ciclo de retroalimentación del desarrollador y deben usarse de manera similar a una prueba de unidad cuando tenga sentido..

Para mostrar una prueba de integración similar a CRUD en acción, escriba la siguiente prueba para probar la añadir El botón coloca a la persona en la colección y que una nueva fila se representa en la plantilla de manillares..

 prueba ('agregar agregará otra persona a la tabla html', function () App.Person.people = []; App.reset (); visit ("/"). luego (function () var rows = find ("tabla tr"). longitud igual (filas, 2, "la tabla tenía" + filas + "filas"); fillIn (". firstName", "foo"); fillIn (". lastName", "bar") ; haga clic en Volver (". submit");). then (function () equal (find ("table tr"). length, 3, "la tabla de personas no estaba completa"); equal (find ("table tr: eq (2) td: eq (0) "). text ()," foo bar "," el nombre completo de la persona era incorrecto ");););

Comience diciéndole a la prueba con qué estado quiere trabajar, luego use el relleno ayudante, añada un nombre y apellido. Ahora si hace clic en el enviar botón debería agregar esa persona a la tabla HTML, por lo que en la devolución entonces Podemos afirmar que existen tres personas en la tabla HTML. Ejecute esta prueba y debería fallar porque el controlador Ember no está completo.

Para obtener la aprobación de la prueba, agregue la siguiente línea a la Controlador de personas

 App.PeopleController = Ember.ArrayController.extend (actions: addPerson: function () var person = firstName: this.get ('firstName'), lastName: this.get ('lastName'); App.Person .add (persona););

Ahora si ejecuta las pruebas usando ./ node_modules / karma / bin / karma start Debería mostrar tres personas en el HTML renderizado..

La última prueba es la eliminación, observe que encontramos el botón para una fila específica y lo presionamos. En el siguiente entonces simplemente verificamos que se muestre una persona menos en la tabla HTML.

 prueba ('eliminar eliminará a la persona de una fila dada', function () App.Person.people = []; App.reset (); visit ("/"). luego (function () var rows = find ("tabla tr"). longitud; igual (filas, 2, "la tabla tenía" + filas + "filas"); haga clic para regresar ("tabla. eliminar: primero");). Luego (función () igual (find ("table tr"). length, 1, "la tabla de personas no estaba completa););")))

Para obtener esta aprobación, simplemente agregue la siguiente línea a la Controlador de personas:

 App.PeopleController = Ember.ArrayController.extend (actions: addPerson: function () var person = firstName: this.get ('firstName'), lastName: this.get ('lastName'); App.Person .add (person);, deletePerson: function (person) App.Person.remove (person););

Ejecute las pruebas desde la línea de comandos y una vez más debe tener un conjunto de pruebas aprobadas..


Conclusión

Así que eso envuelve nuestra aplicación de muestra. Siéntase libre de hacer cualquier pregunta en los comentarios..

Bonus: Pero ya estoy usando Grunt ...

Si prefiere utilizar Grunt en lugar del preprocesador karma-brasa, simplemente elimine los complementos y la configuración de los preprocesadores. También quitar plantillas / *. manillares de la sección de archivos, ya que Karma no necesitará precompilar las plantillas. Aquí hay un simplificado karma.conf.js que funciona cuando se utiliza grunt para precompilar las plantillas de manillares.

 module.exports = function (karma) karma.set (basePath: 'js', archivos: ["lib / deps.min.js", // construido por tu tarea grunt "tests / *. js"], logLevel : karma.LOG_ERROR, navegadores: ['PhantomJS'], singleRun: true, autoWatch: false, frameworks: ["qunit"]); ;

Y eso es!