-
Muy bien, vamos a empezar con una aproximación muy práctica a las pruebas.
-
Las pruebas son algo que, tengo que admitir que hasta hace tres o cuatro ańos
-
Las pruebas son algo que, tengo que admitir que hasta hace tres o cuatro ańos
-
importante, yo creía que era importante, siempre traté de hacerlo, pero hacerlo de esta manera realmente cambió
-
mi vida. Y espero que cambie la vuestra también.
-
Y no, no estoy de campańa presidencial, esto es un... es igual. Muy bien, empecemos hablando de
-
pruebas unitarias. En el libro podéis encontrar algo de información sobre los diferentes tipos de pruebas.
-
Nos centraremos inicialmente en las pruebas unitarias y un poco en las pruebas funcionales,
-
y como con tantas otras cosas en esta clase, existe un acrónimo muy útil que ayuda a recordar
-
cómo deben ser las buenas pruebas. Una de las características es, deben ser rápidas -
-
no debería llevar mucho tiempo ejecutarlas; deben ser independientes,
-
esto es que ejecutar una prueba antes que otra no debería conllevar diferencias
-
esto es que ejecutar una prueba antes que otra no debería conllevar diferencias
-
el orden en el que se ejecuten, no deberían tener dependencias entre ellas;
-
deben ser "repetibles" - si una prueba encuentra un fallo, no debería hacerlo cada vez.
-
En algunos casos esto es fácil de conseguir, en otros es sorprendentemente sutil.
-
Auto-comprobación - lo que las pruebas solían significar no hace tantos para las empresas, es:
-
el software ha llegado de cualquier manera a este departamente de QA, y entonces la gente de QA
-
debería de alguna manera hurgar en él, y hacer cosas, y
"Ah, ya está, eso funcionó, sí, eso funcionó"
-
No vamos a funcionar así más, bien? El código de las pruebas debe conocer él mismo
-
si superó o falló. No debería requerirse intervención humana para tomar esta decisión.
-
Y en un tiempo razonable. Esto significa que las pruebas deben escribirse al mismo tiempo
-
que se escribe el código. Si el código cambia, las pruebas también lo hacen.
-
De hecho, realmente vamos a hacer esto de forma más agresiva.
vamos a escribir las pruebas antes de escribir el código.
-
Esto es, tan a tiempo como puedas. Es como un salto en el tiempo.
-
Qué significa esto?
Porque, si las pruebas son rápidas, por qué necesitamos esto?
-
Bien, es porque podéis ejecutar un subconjunto de las pruebas todo el tiempo.
-
Si tienes miles y miles de pruebas unitarias, lo que no es raro en un proyecto de tamańo medio,
-
podría llevar, ya sabéis, como un minuto o dos ejecutar todo el conjunto de pruebas, lo que podría ralentizaros mucho.
-
Lo ideal es ser capaz de ejecutar rápidamente las pruebas que aplican sobre la parte del código que estáis tocando.
Estáis trabajando, y no queréis nada que os rompa el ritmo.
-
Independientes. Por la misma razón, querríais ser capaces de ejecutar cualquier subconjunto
-
en cualquier orden posible.
-
No sería bueno que existan pruebas que sólo funcionasen adecuadamente
-
si se ejecutasen otras antes.
-
Repetibles. Ya sabéis, otra vez, ejecutándolas N veces, obtener los mismos resultados.
-
Si queréis aislar los fallos y habilitar la depuración automática, la repetibilidad es esencial.
-
Auto-comprobación. Como dije, ningún humano comprobará la salida.
-
Esto significa que podéis tener las pruebas ejecutándose en background todo el tiempo,
-
y siempre que cambies algo que rompa otra cosa a 25 millas de código de distancia,
-
alguna prueba detectará este hecho y te avisará del ello.
-
Y a tiempo. Como dije, vamos a hacer desarrollo basado en las pruebas,
-
donde escribiremos las pruebas antes de escribir el código.
-
Vamos a usar RSpec, que para mí es un lenguaje de dominio específico para escribir pruebas.
-
Para aquellos que no estén familiarizados con DSLs, es básicamente como un pequeńo lenguaje de programación
-
que sólo hace un pequeńo número de cosas en el dominio de una tarea. No pretende ser de propósito general.
-
Ya hemos visto ejemplos de esto. Las migraciones son un tipo de DSL, verdad?
-
Hay un conjunto de sentencias cuyo único trabajo es expresar los cambios en el esquema de la base de datos.
-
Las migraciones son un tipo de DSL que se encuentra embebido en Ruby, esto es,
-
las migraciones son símplemente código en Ruby, pero estilizadas para parecerse a la tarea que hacen.
-
De hecho, veremos que RSpec es un ejemplo similar. Podemos llamarlos DSL internos.
-
Está implementado dentro de otro lenguaje. Las expresiones regulares son otro tipo de DSL interno.
-
Es como un pequeńo sub-vocabulario de cosas que puedes hacer con expresiones regulares.
-
Un ejemplo diferente de DSL es SQL, para hacer consultas a bases de datos.
-
Es un lenguaje propiamente dicho, y aquellos de vosotros que hayáis trabajado con otros frameworks
-
antes de venir a Rails, normalmente habréis terminar teniendo que escribir consultas SQL
-
y después pasarlas como un string a alguien, verdad? Este es un ejemplo muy claro de cómo tratar con diferentes lenguajes.
-
En RSpec, cada prueba se denomina 'spec', viene de 'specification'
-
Sorprendentemente, se encuentran en un directorio llamado 'spec', ya que queremos mantener la simplicidad.
-
Hay un generador en Rails, 'rspec:install' que crea esta estructura de subdirectorios.
-
Todo esto está en el libro, y las asumiremos como leídas para algunas de las demos que haremos hoy
-
para las que ya hemos pasado por estos pasos de configuración.
-
Dónde se encuentra cada cosa? Los directorios bajo spec están diseńados para imitar dónde van las cosas
-
en tu aplicación, ya sabéis: en app/models tenemos los modelos,
-
en spec/models tenemos un fichero spec para cada modelo. Nada nuevo.
-
De la misma manera, tenemos specs para los controladores. Qué pasa con las vistas?
-
Bien, realmente no vamos a hacer specs para ellas. Se pueden hacer, pero son un poco
-
costosas de escribir - mucho de aquello que puedes probar en una vista lo puedes hacer realmente
-
en un spec de controlador, como veremos en un ejemplo hoy.
-
Además, hemos decidido que nuestra solución para escribir estas aplicaciones web de usuario es
-
que construimos historias de usuario para expresar las partes de la aplicación con las que el usuario realmente puede interactuar.
-
Así que, para todas las cosas que son parte de la vista - lo que es visible en la vista,
-
para todo lo que es clickable, etc, usaremos Cucumber.
-
Continuaremos haciendo esto.
-
La mayor parte de tiempo centraremos la atención en RSpec para
-
escribir specs para los modelos y los controladores
-
Empezaremos con un ejemplo de una nueva, hipotética funcionalidad de RottenPotatoes,
-
con la que podremos ańadir películas utilizando información extraída de TMDb.
-
TMDb es un sitio real, se parece mucho a IMDb, pero no es comercial. Es de tipo open-source.
-
La idea es que ellos ya tienen la información de las películas, y lo que queremos
-
es ańadir películas a RottenPotatoes, por que no simplemente sacar la información de allí?
-
Y de hecho, cuando miramos las historias de usuario, existe un paso que dice:
-
"Estoy rellenando términos de búsqueda, voy a buscar la película Inception,
-
y luego presiono el botón que dice 'buscar TMDb', no es así?
-
Se supone que existe un botón que de alguna manera me llevará a TMDb,
-
mirará si Inception está allí, y nos traerá de vuelta.
-
Así que la pregunta es, qué tenemos que hacer, qué tipo de código debemos escribir,
-
y que tipo de prueba es necesaria para convertir esta sentencia que está en rojo a algo funcional?
-
Antes de lanzarnos a ello, recordemos lo que hablamos sobre "la Cocina de Rails",
-
el tipo de recetas para hacer esto en Rails? Recordad que cuando ańadimos una nueva funcionalidad,
-
ańadimos una nueva ruta, necesitamos un nuevo método controlador y
-
quizá necesitemos una nueva vista, dependiendo de si una existente puede reutilizarse
-
o tenemos que hacer algo nuevo. Pero estos son pasos que siempre seguimos.
-
Bueno, empecemos de una vez.
-
Esta es una idea que veremos muchas, muchas veces; Empezaré a acostumbraros a la frase.
-
Esta es, "El código que te gustaría tener". Es una idea inmensamente poderosa una vez que te haces a ella.
-
Es un poco extrańo hacer las cosas de esta manera cuando empiezas, pero es muy poderosa.
-
Así que nos preguntamos: "Bien, cuando un usuario hace click en ese botón para 'buscar en TMDb', sabemos que
-
en algún sitio habrá una acción de controlador que recibirá la información.
-
Así que, qué debería hacer este método? Que debería hacer el método controlador cuando reciba el formulario de búsqueda?
-
Bueno, si preguntáis, en inglés - si escribimos lo que debería ser,
-
deberíamos decir: "Bien, veamos, debería llamar a algún método (que no hemos escrito aún)
-
que realmente iría a TMDb y buscaría la película". Tiene sentido.
-
"Si encuentra una película, debería renderizar algún tipo de vista con los resultados de la búsqueda para enseńar las coincidencias
-
(otra vez, aún no hemos creado la vista, pero lógicamente, esto es lo que vamos a
-
escribir que haremos)." No vamos a tener tiempo de hacer el paso tres hoy,
-
pero el 'sad path' es algo como, "Si no coincide, debería redireccionar a la hompage de RottenPotatoes y decir,
-
'No se encontraron coincidencias'. " Y si miráis el ejemplo en el libro,
-
realmente escribimos este 'sad path' en el capítulo de BDD.
-
Así que, dado que estas dos cosas - centrémonos en #1 y #2 - son las que el método controlador debería hacer,
-
así es como deberíamos expresarlo - dónde está mi ratón? Vamos allá-
-
Así es como expresaríamos todo esto usando RSpec. Y, bien. Entonces.
-
No hemos hecho demasiado, y ya sabéis, es bonito y rápido, verdad?
-
Y este es código RSpec legal. SpecHelper es simplemente un fichero que RSpec crea como parte del paso de instalación.
-
Hace algunas verificaciones para asegurarse de que todo está cargado.
-
Así que empezaremos diciendo, "Sobre qué trata esta prueba?"
-
O bien, vamos a describir el comportamiento de MoviesController,
-
y el controlador va a tener diferentes comportamientos, pero el que nos interesa aquí
-
es el comportamiento de búqueda en TMDb. Así que, podéis impaginar, que para cada comportamiento de controlador,
-
nuestra especificación crece, vamos a ańadir más bloques descriptivos dentro de este,
-
'describe MoviesController', y podéis seguir anidando, anidando y anidando.
-
'It' es realmente una llamada a un método en RSpec. Recibe un argumento, que es
-
un string describiendo lo que debería pasar. Y como veremos en un momento,
-
también recibe un segundo argumento: un procedimiento que realiza el test.
-
también recibe un segundo argumento: un procedimiento que realiza el test.
-
Pero por ahora, todo lo que hemos hecho ha sido transliterar. Pensamos en las tres cosas
-
que el método controlador debería hacer, y escribimos tres cosas en RSpec.
-
Esto es de hecho suficiente para empezar, y existe un screencast que os recomiendo que veáis
-
que está asociado con el capítulo del libro, que hace exactamente esto.
-
Todo lo que hace es ejecutar tres pruebas, y estas no hacen nada, así que RSpec las pinta en amarillo, verdad?
-
Amarillo significa 'aún no implementado', igual que para Cucumber.
Todo lo que he hecho ha sido transcribir las tres cosas que dijimos que debería hacer.
-
Así que, gran acrónimo!