Prueba de JavaScript desde cero

Probablemente este no sea el primer tutorial sobre pruebas que hayas visto. Pero quizás haya tenido sus dudas sobre las pruebas y nunca se haya tomado el tiempo para leerlas. Después de todo, puede parecer un trabajo extra sin ninguna razón..

Este tutorial pretende cambiar tus puntos de vista. Vamos a comenzar desde el principio: ¿qué es la prueba y por qué debería hacerlo? Luego, hablaremos brevemente sobre la escritura de código verificable, antes de que se haga algunas pruebas. Hagámoslo!


Prefiero un Screencast?

Parte 1


Parte 2


Parte 3



Definiendo pruebas

Muy simple, pruebas es la idea de tener un conjunto de requisitos que una determinada pieza de código debe pasar para que sea lo suficientemente robusta como para ser utilizada en el mundo real. A menudo, escribimos algo de JavaScript y luego lo abrimos en el navegador y hacemos clic un poco para asegurarnos de que todo funciona como esperábamos. Aunque a veces es necesario, ese no es el tipo de prueba del que estamos hablando aquí. De hecho, espero que este tutorial lo convenza de que la autoevaluación rápida y sucia debería complementar un procedimiento de prueba más rígido: la autoprueba está bien, pero una lista completa de requisitos es primordial.


Razones para la prueba

Como puede imaginar, el problema con las pruebas de JavaScript de actualizar y hacer clic alrededor es doble:

  1. Puede que no recordemos revisar algo; incluso si lo hacemos, es posible que no volvamos a revisar luego de los ajustes de código.
  2. Puede haber algunas partes del código que no son realmente verificables de esa manera.

Por escrito pruebas Para comprobar todo lo que debería hacer su código, puede verificar que su código esté en la mejor forma antes de utilizarlo en un sitio web. Para cuando algo se está ejecutando en un navegador, es probable que haya varios puntos de falla. Las pruebas de escritura le permiten concentrarse en cada parte comprobable individualmente; Si cada pieza hace su trabajo correctamente, las cosas deberían funcionar juntas sin problema (probar partes individuales como esta se llama prueba de unidad).


Escribir código comprobable

Si eres un políglota de programación), es posible que hayas hecho pruebas en otros idiomas. Pero he encontrado pruebas en JavaScript una bestia diferente para matar. Después de todo, no está construyendo demasiadas interfaces de usuario en, por ejemplo, PHP o Ruby. A menudo, estamos haciendo trabajo de DOM en JavaScript, y exactamente cómo lo pruebas?

Bueno, el trabajo de DOM no es para lo que quieres escribir pruebas; es la logica Obviamente, entonces, la clave aquí es separar su lógica y su código de UI. Esto no siempre es fácil; He escrito mi parte justa de la interfaz de usuario con jQuery, y se puede ensuciar bastante rápido. Esto no solo hace que sea difícil de probar, sino que la lógica entrelazada y el código UI también pueden ser difíciles de modificar cuando cambia el comportamiento deseado. He encontrado que el uso de metodologías como plantillas (también, plantillas) y pub / sub (también, pub / sub) hace que la escritura sea mejor, el código más fácil de probar.

Una cosa más, antes de comenzar a codificar: ¿cómo escribimos nuestras pruebas? Hay numerosas bibliotecas de prueba que puede usar (y muchos buenos tutoriales para enseñarle a usarlos; vea los enlaces al final). Sin embargo, vamos a construir una pequeña biblioteca de pruebas desde cero. No será tan elegante como algunas bibliotecas, pero podrá ver exactamente lo que está pasando.

Con esto en mente, vamos a trabajar!


Construyendo un Mini-Marco de Prueba

Vamos a construir una micro galería de fotos: una simple lista de miniaturas, con una imagen que se muestra en tamaño completo sobre ellas. Pero primero, vamos a construir una función de prueba.

A medida que obtenga más información sobre las pruebas y las bibliotecas de pruebas, encontrará numerosos métodos de prueba para probar todo tipo de aspectos específicos. Sin embargo, todo puede reducirse a si dos cosas son iguales o no: por ejemplo,

  • ¿El valor devuelto por esta función es igual al que esperábamos recuperar??
  • ¿Es esta variable del tipo que esperábamos que fuera??
  • ¿Esta matriz tiene el número esperado de elementos??

Entonces, aquí está nuestro método para probar la igualdad:

var TEST = areEqual: function (a, b, msg) var result = (a === b); console.log ((result? "PASS:": "FAIL:") + msg); resultado de retorno ;

Es bastante simple: el método toma tres parámetros. Los dos primeros se comparan, y si son iguales, las pruebas pasan. El tercer parámetro es un mensaje que describe la prueba. En esta sencilla biblioteca de pruebas, solo estamos enviando nuestras pruebas a la consola, pero puede crear una salida HTML con el estilo CSS adecuado si lo desea.

Aquí esta la areNotEqual método (dentro del mismo PRUEBA objeto):

areNotEqual: function (a, b, msg) var result = (a! == b); console.log ((result? "PASS:": "FAIL:") + msg); resultado de retorno 

Notarás las dos últimas líneas de son iguales y areNotEqual lo mismo. Entonces, podemos sacarlos así:

var TEST = areEqual: function (a, b, msg) return this._output (a === b, msg), areNotEqual: function (a, b, msg) return this._output (a! == b, msg); , _output: function (result, msg) console [result? "log": "warn"] ((result? "PASS:": "FAIL:") + msg); resultado de retorno ;

¡Genial! Lo bueno aquí es que podemos agregar otros métodos de "azúcar" usando los métodos que ya hemos escrito:

TEST.isTypeOf = function (obj, type, msg) return this.areEqual (typeof obj, type, msg); ; TEST.isAnInstanceOf = function (obj, type, msg) return this._output (obj instanceof type, msg);  TEST.isGreaterThan = function (val, min, msg) return this._output (val> min, msg); 

Puedes experimentar con esto por tu cuenta; Después de seguir este tutorial, tendrás una buena idea de cómo usarlo..


Preparándonos para nuestra galería

Entonces, vamos a crear una galería de fotos súper simple, usando nuestro mini PRUEBA Marco para crear algunas pruebas. Aquí mencionaré que si bien el desarrollo basado en pruebas es una gran práctica, no lo usaremos en este tutorial, principalmente porque no es algo que pueda aprender en un solo tutorial; se necesita mucha práctica para realmente gruñir. Cuando empiezas, es más fácil escribir un poco de código y luego probarlo.

Entonces, vamos a empezar. Por supuesto, necesitaremos algo de HTML para nuestra galería. Vamos a mantener esto bastante básico:

     Pruebas en JavaScript     

Hay dos cosas principales que vale la pena notar aquí: primero, tenemos un

que contiene el marcado muy simple para nuestra galería de imágenes. No, probablemente no sea muy robusto, pero nos da algo con lo que trabajar. Entonces, note que estamos enganchando tres. >s: one es nuestra pequeña librería de pruebas, como se encuentra arriba. Una es la galería que crearemos. El último lleva a cabo las pruebas para nuestra galería. Observe también las rutas de acceso a las imágenes: los nombres de archivo en miniatura tienen "-tumbas" añadidas a ellas. Así es como encontraremos la versión más grande de la imagen..

Sé que tienes ganas de codificar, así que mete esto en un galería.css expediente:

.galería fondo: #ececec; desbordamiento: oculto; ancho: 620px;  .gallery> img margin: 20px 20px 0; relleno: 0;  .gallery ul list-style-type: none; margen: 20px; relleno: 0; desbordamiento: oculto;  .gallery li float: left; margen: 0 10px;  .gallery li: first-of-type margin-left: 0px;  .gallery li: last-of-type margin-right: 0px; 

Ahora, puedes cargar esto en tu navegador y ver algo como esto:

¡OKY YA! Vamos a escribir algunos JavaScript, vamos a?


Esbozando nuestra galería

Abre eso galeria.js expediente. Así es como empezamos:

var Gallery = (function () var Gallery = , galleryPrototype = ; Gallery.create = function (id) var gal = Object.create (galleryPrototype); return gal;; return Gallery; ()) ;

Estaremos agregando más, pero este es un buen comienzo. Hemos usado una función anónima que invoca a sí misma (o una expresión de función invocada de inmediato) para mantener todo junto. Nuestro “interno” Galería La variable será devuelta y será el valor de nuestro público. Galería variable. Como puedes ver, llamando. Galería.crear creará un nuevo objeto de galería con Object.create. Si no estás familiarizado con Object.create, simplemente crea un nuevo objeto utilizando el objeto al que lo pasas como prototipo del nuevo objeto (también es bastante compatible con el navegador). Rellenaremos ese prototipo y lo añadiremos a nuestro Galería.crear método también. Pero, ahora vamos a escribir nuestra primera prueba:

var gal = Gallery.create ("gal-1"); TEST.areEqual (typeof gal, "object", "Gallery debería ser un objeto");

Comenzamos creando una “instancia” de Galería; luego, ejecutamos una prueba para ver si el valor devuelto es un objeto.

Pon estas dos líneas en nuestra galería-test.js; ahora abre nuestro index.html página en un navegador y abrir una consola de JavaScript. Debería ver algo como esto:


¡Genial! Nuestra primera prueba está pasando!


Escribiendo el constructor

A continuación, rellenaremos nuestra Galería.crear método. Como verá, no nos preocupamos por hacer que este código de ejemplo sea súper robusto, por lo que usaremos algunas cosas que no son compatibles en todos los navegadores creados. A saber, document.querySelector / document.querySelectorAll; Además, solo usaremos el manejo moderno de eventos del navegador. Siéntase libre de sustituir su biblioteca favorita si lo desea.

Entonces, comencemos con algunas declaraciones:

var gal = Object.create (galleryPrototype), ul, i = 0, len; gal.el = document.getElementById (id); ul = gal.el.querySelector ("ul"); gal.imgs = gal.el.querySelectorAll ("ul li img"); gal.displayImage = gal.el.querySelector ("img: first-child"); gal.idx = 0; gal.going = falso; gal.ids = [];

Cuatro variables: notablemente, el objeto de nuestra galería y el de la galería.

    nodo (usaremos yo y len en un minuto). Entonces, seis propiedades en nuestra galón objeto:

    • gal.el es el nodo "raíz" en markyp de nuestra galería.
    • gal.imgs es una lista de
    • s que sostienen nuestras miniaturas.
    • gal.displayImage Es la imagen grande en nuestra galería..
    • gal.idx Es el índice, la imagen actual que se está viendo..
    • gal.going es un booleano: es cierto Si la galería está recorriendo las imágenes..
    • gal.ids Será una lista de las ID de las imágenes en nuestra galería. Por ejemplo, si la miniatura se llama "dog-thumb.jpg", entonces "dog" es la ID y "dog.jpg" es la imagen de tamaño de pantalla.

    Tenga en cuenta que los elementos DOM tienen querySelector y querySelectorAll métodos también. Nosotros podemos usar gal.el.querySelector para asegurarnos de que solo estamos seleccionando elementos dentro del marcado de esta galería.

    Ahora, completa gal.ids con las identificaciones de la imagen:

    len = gal.imgs.length; para (; i < len; i++ )  gal.ids[i] = gal.imgs[i].getAttribute("src").split("-thumb")[0].split("/")[1]; 

    Bastante sencillo, a la derecha?

    Finalmente, conectemos un controlador de eventos en el

      .

      ul.addEventListener ("clic", función (e) var i = [] .indexOf.call (gal.imgs, e.target); if (i> -1) gal.set (i);, falso);

      Comenzamos por verificar si el elemento más bajo que recibió el clic (e.target; no nos preocupa el soporte de oldIE aquí) está en nuestra lista de imágenes; ya que NodeLists no tiene un índice de método, utilizaremos la versión de matriz (si no está familiarizado con llamada y aplicar En JavaScript, vea nuestro consejo rápido sobre ese tema. Si eso es mayor que -1, lo pasaremos a gal.set. No hemos escrito este método todavía, pero lo haremos.

      Ahora, volvamos a nuestro galería-test.js Archivo y escribir algunas pruebas para asegurarse de que nuestra Galería instancia tiene las propiedades adecuadas:

      TEST.areEqual (gal.el.id, "gal-1", "Gallery.el debe ser el que especificamos"); TEST.areEqual (gal.idx, 0, "El índice de la galería debe comenzar en cero");

      Nuestra primera prueba verifica que nuestro constructor de galerías encontró el elemento correcto. La segunda prueba verifica que el índice comienza en 0. Probablemente podría escribir un montón de pruebas para verificar que tenemos las propiedades correctas, pero estaremos escribiendo pruebas para los métodos que usarán estas propiedades, por lo que realmente no. ser necesario.


      Construyendo el Prototipo

      Ahora, volvamos a eso galeríaprototipo objeto que está actualmente vacío. Aquí es donde alojaremos todos los métodos de nuestra Galería “Instancias”. Empecemos por la conjunto Método: este es el método más importante, porque es el que realmente cambia la imagen mostrada. Toma el índice de la imagen o la cadena de identificación de la imagen.

      // dentro de 'galleryProtytype' set: function (i) if (typeof i === 'string') i = this.ids.indexOf (i);  this.displayImage.setAttribute ("src", "images /" + this.ids [i] + ".jpg"); return (this.idx = i); 

      Si el método obtiene la cadena de ID, encontrará el número de índice correcto para esa ID. Entonces, establecemos la mostrar imagenes src a la ruta de la imagen correcta, y devuelve el nuevo índice actual mientras lo configura como el índice actual.

      Ahora, vamos a probar ese método (de nuevo en galería-test.js):

      TEST.areEqual (gal.set (4), 4, "Gallery.set (con número) debe devolver el mismo número pasado en"); TEST.areEqual (gal.displayImage.getAttribute ("src"), "images_24 / javascript-testing-from-scratch.jpg", "Gallery.set (con número) debe cambiar la imagen mostrada"); TEST.areEqual (gal.set ("post"), 3, "Gallery.set (con cadena) debe moverse a la imagen correspondiente"); TEST.areEqual (gal.displayImage.getAttribute ("src"), "images / post.jpg", "Gallery.set (con cadena) debe cambiar las imágenes mostradas");

      Probamos nuestro método de prueba con un número y un parámetro de cadena para conjunto. En este caso, podemos comprobar el src para la imagen y asegúrese de que la interfaz de usuario se ajusta en consecuencia; no siempre es posible o necesario asegurarse de que lo que el usuario está viendo responda adecuadamente (sin usar algo como esto); ahí es donde el tipo de prueba de click-around es útil. Sin embargo, podemos hacerlo aquí, así que lo haremos.

      Esas pruebas deben pasar. También debería poder hacer clic en las miniaturas y mostrar las versiones más grandes. Luce bien!

      Entonces, pasemos a algunos métodos que se mueven entre las imágenes. Estos podrían ser útiles si quisiera tener los botones "siguiente" y "anterior" para recorrer las imágenes (no tendremos estos botones, pero pondremos los métodos de soporte).

      En su mayor parte, pasar a la imagen siguiente y anterior no es una cosa difícil. Las partes difíciles van a la siguiente imagen cuando estás en la última, o la anterior cuando estás en la primera imagen..

      // dentro de 'galleryPrototype' siguiente: function () if (this.idx === this.imgs.length - 1) return this.set (0);  devuelve this.set (this.idx + 1); , prev: function () if (this.idx === 0) return this.set (this.imgs.length - 1);  devuelve this.set (this.idx - 1); , curr: function () return this.idx; ,

      Bueno, en realidad no es demasiado complicado. Ambos métodos son métodos de “azúcar” para usar conjunto. Si estamos en la última imagen (this.idx === this.imgs.length -1), nosotros establecer (0). Si estamos en la primera (this.idx === 0), nosotros conjunto (this.imgs.length -1). De lo contrario, simplemente agregue o reste uno del índice actual. No olvide que estamos devolviendo exactamente lo que se devuelve desde el conjunto llamada.

      También tenemos el curr método, también. No es complicado en absoluto: simplemente devuelve el índice actual. Lo probaremos un poco más tarde..

      Entonces, vamos a probar estos métodos.

      TEST.areEqual (gal.next (), 4, "La galería debe avanzar en .next ()"); TEST.areEqual (gal.prev (), 3, "La galería debe volver en .prev ()");

      Estos vienen después de nuestras pruebas anteriores, por lo que 4 y 3 son los valores que esperaríamos. Y pasan!

      Solo queda una pieza: ese es el ciclismo automático de fotos. Queremos poder llamar gal.start () jugando a través de las imágenes. Por supuesto, serán un gal.stop () método.

      // dentro de 'galleryPrototype' start: function (time) var thiz = this; tiempo = tiempo || 3000; this.interval = setInterval (function () thiz.next ();, time); this.going = true; devuelve verdadero , stop: function () clearInterval (this.interval); this.going = false; devuelve verdadero ,

      Nuestro comienzo El método tomará el parámetro: el número de milisegundos que se muestra una imagen; Si no se da ningún parámetro, por defecto es 3000 (3 segundos). Entonces, simplemente usamos setInterval en una función que llamará siguiente en el momento oportuno. Por supuesto, no podemos olvidar de configurar esto. en marcha a cierto. Finalmente volvemos cierto.

      detener no es demasiado dificil Desde que guardamos el intervalo como este.intervalo, nosotros podemos usar clearInterval para acabarlo Entonces, nos propusimos esto. en marcha a falso y devuelve verdadero.

      Tendremos dos funciones prácticas para avisar si la galería está en bucle:

      isGoing: function () return this.going; , isStopped: function () return! this.going; 

      Ahora, probemos esta funcionalidad.

      gal.set (0); TEST.areEqual (gal.start (), true, "La galería debería estar en bucle"); TEST.areEqual (gal.curr (), 0, "El índice de imagen actual debe ser 0"); setTimeout (function () TEST.areEqual (gal.curr (), 1, "El índice de imagen actual debe ser 1"); TEST.areEqual (gal.isGoing (), verdadero, "La galería debería estar en marcha"); TEST. areEqual (gal.stop (), true, "La galería debe detenerse"); setTimeout (function () TEST.areEqual (gal.curr (), 1, "La imagen actual aún debe ser 1"); TEST.areEqual ( gal.isStopped (), true, "La galería aún debería detenerse");, 3050);, 3050);

      Es un poco más complicado que nuestros conjuntos de pruebas anteriores: Comenzamos usando gal.set (0) para asegurarnos de que estamos empezando por el principio. Entonces, llamamos gal.start () para iniciar el bucle. A continuación, probamos que gal.curr () devuelve 0, lo que significa que todavía estamos viendo la primera imagen. Ahora usaremos un setTimeout Esperar 3050ms (solo un poco más de 3 segundos) antes de continuar con nuestras pruebas. Dentro de eso setTimeout, haremos otro gal.curr (); el índice ahora debería ser 1. Entonces, probaremos que gal.isGoing () es verdad. A continuación, detendremos la galería. gal.stop (). Ahora usamos otro setTimeout Esperar otros casi 3 segundos; Si la galería realmente se ha detenido, la imagen no se reproducirá en bucle. gal.curr () aún debe ser 1; eso es lo que probamos dentro del tiempo muerto. Finalmente, nos aseguramos de que nuestra está parado el método está funcionando.

      Si estas pruebas han pasado, felicidades! Hemos completado nuestro Galería y sus pruebas.


      Conclusión

      Si no ha probado las pruebas anteriormente, espero que haya visto cómo las pruebas pueden ser simples en JavaScript. Como mencioné al comienzo de este tutorial, es probable que las buenas pruebas requieran que escriba su JavaScript de forma un poco diferente a lo que podría estar acostumbrado. Sin embargo, he encontrado que JavaScript fácilmente comprobable también es fácil de mantener JavaScript.

      Te dejo con varios enlaces que te pueden resultar útiles a medida que avanzas y escribes buenos JavaScript y buenas pruebas.

      • QUnit - un conjunto de pruebas de unidad (Tutorial sobre QUnit)
      • Jasmine - BDD framework (Tutorial sobre Jasmine)
      • Prueba de JavaScript con Assert
      • Test Driven JavaScript (libro) (capítulo de muestra)