Como pruebo

En una discusión reciente en Google+, un amigo mío comentó: "El desarrollo dirigido por pruebas (TDD) y el desarrollo dirigido por el comportamiento (BDD) son Ivory Tower BS."Esto me llevó a pensar en mi primer proyecto, cómo me sentía de la misma manera en ese momento y cómo me siento al respecto. Desde ese primer proyecto, he desarrollado un ritmo de TDD / BDD que no solo funciona para mí, sino que para el cliente tambien.

Ruby on Rails se envía con un conjunto de pruebas, llamado Test Unit, pero muchos desarrolladores prefieren usar RSpec, Cucumber o alguna combinación de los dos. Personalmente, prefiero este último, utilizando una combinación de ambos..


RSpec

Desde el sitio RSpec:

RSpec es una herramienta de prueba para el lenguaje de programación Ruby. Nacido bajo la bandera de Behavior-Driven Development, está diseñado para hacer de Test-Driven Development una experiencia productiva y agradable.

RSpec proporciona un DSL potente que es útil para pruebas de unidad e integración. Si bien he usado RSpec para escribir pruebas de integración, prefiero usarlo solo en una unidad de capacidad de prueba. Por lo tanto, cubriré cómo uso RSpec exclusivamente para pruebas unitarias. Recomiendo leer El libro RSpec de David Chelimsky y otros para una cobertura completa y detallada de RSpec.


Pepino

He encontrado que los beneficios de TDD / BDD superan con creces las desventajas.

Cucumber es un marco de pruebas de integración y aceptación que admite Ruby, Java, .NET, Flex y muchos otros lenguajes y marcos web. Su verdadero poder proviene de su DSL; No solo está disponible en inglés simple, sino que se ha traducido a más de cuarenta idiomas hablados..

Con una prueba de aceptación legible por humanos, puede hacer que el cliente firme una función antes de escribir una sola línea de código. Al igual que con RSpec, solo cubriré a Pepino en la capacidad en que lo uso. Para el resumen completo de Cucumber, echa un vistazo a The Cucumber Book.


La puesta en marcha

Primero comencemos un nuevo proyecto, indicando a Rails que se salte la unidad de prueba. Escriba lo siguiente en un terminal:

 rieles nuevos how_i_test -T

Dentro de Gemfile, añadir:

 fuente 'https: //rubygems.org' grupo: prueba hacer gema 'capibara' gema 'pepinos-rieles', requiere: gema falsa 'limpiador de base de datos' gema 'factory_girl_rails' gema 'shoulda' grupo final: desarrollo,: prueba hacer Gema 'rspec-rails final

Principalmente utilizo RSpec para asegurar que mis modelos y sus métodos se mantengan en jaque..

Aquí, hemos puesto a Pepino y amigos dentro del grupo. prueba bloquear. Esto garantiza que se carguen correctamente solo en el entorno de prueba de Rails. Observe cómo también cargamos RSpec dentro de la desarrollo y prueba Bloques, haciéndolo disponible en ambos entornos. Hay algunas otras gemas. que detallaré brevemente a continuación. No te olvides de correr instalación de paquete para instalarlos.

  • Capibara: simula las interacciones del navegador.
  • Limpiador de base de datos: limpia la base de datos entre ejecuciones de prueba.
  • Factory Girl Rails: reemplazo de accesorio.
  • Shoulda: métodos de ayuda y comparadores para RSpec.

Necesitamos ejecutar los generadores de estas gemas para configurarlos. Puedes hacerlo con los siguientes comandos de terminal:

 rieles g rspec: instalar crear .rspec crear espec. crear espec. / spec_helper.rb rieles g cucumber: instalar crear config / cucumber.yml crear script / cucumber chmod script / cucumber crear características / step_definitions crear características / soporte crear características / soporte / env. rb existen lib / tasks create lib / tasks / cucumber.rake gsub config / database.yml gsub config / database.yml force config / database.yml

En este punto, podríamos comenzar a escribir especificaciones y señales para probar nuestra aplicación, pero podemos configurar algunas cosas para facilitar las pruebas. Vamos a empezar en el aplicacion.rb expediente.

 módulo HowITest class Aplicación < Rails::Application config.generators do |g| g.view_specs false g.helper_specs false g.test_framework :rspec, :fixture => true g.fixture_replacement: factory_girl,: dir => 'spec / factories' end ... end end end

Dentro de la clase de aplicación, anulamos algunos de los generadores predeterminados de Rails. Para los dos primeros, omitimos las vistas y las especificaciones de generación de ayudantes..

Estas pruebas no son necesarias, porque solo estamos usando RSpec para pruebas unitarias.

La tercera línea informa a Rails que tenemos la intención de utilizar RSpec como nuestro marco de prueba de prueba, y también debe generar accesorios al generar modelos. La línea final asegura que usamos factory_girl para nuestros accesorios, de los cuales se crean en el especificaciones / fábricas directorio.


Nuestra primera característica

Para simplificar las cosas, vamos a escribir una característica simple para iniciar sesión en nuestra aplicación. En aras de la brevedad, me saltearé la implementación real y me quedaré con el conjunto de pruebas. Aquí está el contenido de características / firma_en.featura:

 Característica: Iniciar sesión Para utilizar la aplicación Como usuario registrado, deseo iniciar sesión a través de un formulario. Escenario: Iniciar sesión a través del formulario. Dado que hay un usuario registrado con el correo electrónico "[email protected]". en la página Cuando ingrese las credenciales correctas Y presiono el botón de inicio de sesión Luego, el mensaje flash debe ser "Iniciar sesión correctamente".

Cuando ejecutamos esto en la terminal con Características del pepino, Vemos muchos resultados que terminan con nuestros pasos indefinidos:

 Dado que ^ hay un usuario registrado con correo electrónico "(. *?)" $ / Do | arg1 | pendiente # exprese la expresión regular anterior con el código que desea que finalice Dado / ^ Estoy en la página de inicio de sesión $ / do pendiente # exprese la expresión regular anterior con el código que desea que finalice Cuando / ^ ingrese las credenciales correctas $ / hacer pendiente # expresar la expresión regular anterior con el código que desea que finalice Cuando / ^ presiono el botón de inicio de sesión $ / hacer pendiente # expresar la expresión regular anterior con el código que desea que finalice Luego / ^ el mensaje flash debería ser " (. *?) "$ / do | arg1 | pendiente # expresa la expresión regular anterior con el código que deseas que finalice

El siguiente paso es definir qué esperamos que haga cada uno de estos pasos. Expresamos esto en características / stepdefinitions / signin_steps.rb, Usando Ruby simple con selectores Capybara y CSS.

 Dado que ^ ^ hay un usuario registrado con correo electrónico "(. *?)" $ / Do | email | @user = FactoryGirl.create (: user, email: email) final Dado / ^ Estoy en la página de inicio de sesión $ / do visit sign_in_path end Cuando / ^ Ingrese las credenciales correctas $ / do fillin "Email", con: @user .Informe rellenar "Contraseña", con: @ user.password fin Cuando / ^ Presiono el botón de inicio de sesión $ / do click_button "Iniciar sesión" fin Luego / ^ el mensaje flash debería ser "(. *?)" $ / do | texto | dentro de (". flash") hacer página. debería tener contenido de contenido contenido final

Dentro de cada uno de los Dado, Cuando, y Entonces bloques, utilizamos el DSL de Capybara para definir lo que esperamos de cada bloque (excepto en el primero). En el primer bloque dado, le decimos a factory_girl que cree un usuario almacenado en el usuario Variable de instancia para su uso posterior. Si tu corres Características del pepino De nuevo, deberías ver algo similar a lo siguiente:

 Escenario: Iniciar sesión a través del formulario # features / signature_in.feature: 6 Dado que hay un usuario registrado con el correo electrónico "[email protected]" # features / step_definitions / signature \ _in \ _steps.rb: 1 Factory no registrado: user ( ArgumentError) ./features/step_definitions/signing\_in\_steps.rb:2:in '/ ^ hay un usuario registrado con correo electrónico "(. *?)" $ /' Features / sign_in.feature: 7: in 'Given hay un usuario registrado con el correo electrónico "[email protected]"

Podemos ver en el mensaje de error que nuestro ejemplo falla en la línea 1 con un ArgumentError de la fábrica del usuario no está registrado. Podríamos crear esta fábrica nosotros mismos, pero parte de la magia que configuramos anteriormente hará que Rails haga eso por nosotros. Cuando generamos nuestro modelo de usuario, obtenemos la fábrica de usuarios de forma gratuita.

 rails g modelo correo electrónico del usuario: cadena contraseña: cadena invoque active_record create db / migrate / 20121218044026 \ _create \ _users.rb create app / models / user.rb invoke rspec create spec / models / user_spec.rb invoke factory_girl create spec / fábricas / usuarios .rb

Como puedes ver, el generador de modelos invoca factory_girl y crea el siguiente archivo:

ruby spec / factories / users.rb FactoryGirl.define do factory: user do email "MyString" password "MyString" end end

No voy a entrar en gran profundidad de factory_girl aquí, pero puedes leer más en su guía de introducción. No te olvides de correr rastrillo db: migrar y rastrillo db: prueba: preparar para cargar el nuevo esquema. Esto debería hacer que pase el primer paso de nuestra función, y empezar a usar Cucumber para sus pruebas de integración. En cada paso de tus funciones, Cucumber te guiará hacia las piezas que ve que faltan para hacerlas pasar..


Pruebas de modelos con RSpec y Shoulda

Principalmente utilizo RSpec para asegurarme de que mis modelos y sus métodos se mantengan controlados. A menudo también lo uso para algunas pruebas de controlador de alto nivel, pero eso se explica con más detalle de lo que permite esta guía. Vamos a utilizar el mismo modelo de usuario que configuramos previamente con nuestra función de inicio de sesión. Mirando hacia atrás en la salida de ejecutar el generador de modelo, podemos ver que también tenemos usuario_spec.rb gratis. Si corremos rspec spec / models / user_spec.rb deberíamos ver la siguiente salida.

 Pendiente: el usuario agrega algunos ejemplos a (o elimina) /Users/janders/workspace/how\_i\_test/spec/models/user_spec.rb

Y si abrimos ese archivo, vemos:

 requiera 'spechelper' describa Usuario que está pendiente "agregue algunos ejemplos para (o elimine) # ARCHIVO" final

La línea pendiente nos da la salida que vimos en el terminal. Aprovecharemos los emparejadores ActiveRecord y ActiveModel de Shoulda para garantizar que nuestro modelo de usuario coincida con nuestra lógica empresarial.

 requiera 'spechelper' describa el usuario haga el contexto "#fields" hágalo debería responder a (: correo electrónico) esto debería responder a (: contraseña) esto debería responder a (: nombre) esto debe responder a (: apellido) fin el contexto "#validaciones" hágalo debería validar_presencia_de (: correo electrónico) él debería validar_presencia_de (: contraseña) él debería validar_uniquidad_ ((correo electrónico) fin de contexto "#asociaciones" hacerlo debería tener_ muchas (: tareas) fin describe "#methods" do let! (: user) FactoryGirl.create (: user) it "name debe devolver el nombre de los usuarios" do user.name.should eql "Testy McTesterson" end end end end

Configuramos unos cuantos bloques de contexto dentro de nuestro primer describir Bloque para probar cosas como campos, validaciones y asociaciones. Si bien no hay diferencias funcionales entre un describir y un contexto Bloque, hay un contexto. Usamos describir Bloques para establecer el estado de lo que estamos probando, y contexto Bloques para agrupar esas pruebas. Esto hace que nuestras pruebas sean más legibles y mantenibles a largo plazo..

El primero describir nos permite probar contra el Usuario modelo en un estado no modificado.

Usamos este estado no modificado para probar contra la base de datos con los emparejadores de Shoulda agrupándolos por tipo. El siguiente describir bloque configura un usuario de nuestro creado previamente usuario fábrica. Configurando el usuario con el dejar El método dentro de este bloque nos permite probar una instancia de nuestro modelo de usuario con atributos conocidos.

Ahora, cuando corremos rspec spec / models / user_spec.rb, Vemos que todas nuestras nuevas pruebas fallan..

 Fallos: 1) El nombre del método # del usuario debe devolver el nombre del usuario. Error / Error: usuario.nombre.se debe eql "Testy McTesterson" NoMethodError: nombre de método indefinido 'para # # ./spec/models/user_spec.rb:26:inbloque (3 niveles) en '2) Error / error de validación de # de usuario: it debe validate_uniqueness_of (: email) Se espera que los errores incluyan "ya se ha tomado" cuando el correo electrónico está configurado como "arbitrariocadena ", no tiene errores # ./spec/models/userspec.rb: 15: en 'bloque (3 niveles) en '3) Error / error de validación de # de usuario: it debe validate_presence_of (: password) Se espera que los errores incluyan "no puede estar en blanco" cuando la contraseña se establece en nulo, no se detectaron errores # ./spec/models/user_spec.rb : 14: en 'bloque (3 niveles) en '4) Error / error de validación de # de usuario: it debe validate_presence_of (: email) Se espera que los errores incluyan "no puede estar en blanco" cuando el correo electrónico está configurado como nulo, no obtuvo errores # ./spec/models/user_spec.rb : 13: en 'bloque (3 niveles) en '5) Número de asociaciones de usuarios Error / Error: debe debería tenermuchos (: tareas) Usuario esperado para tener un tienemuchas asociaciones llamadas tareas (ninguna asociación llamadas tareas) # ./spec/models/user_spec.rb:19:in 'bloque (3 niveles) en '6) campos # de usuario Error / Error: debería responderdurarnombre) esperado # responder a: ultimonombre # ./spec/models/userspec.rb: 9: en 'bloque (3 niveles) en '7) campos # de usuario Error / Error: debería responderprimeronombre) esperado # responder a: primeronombre # ./spec/models/userspec.rb: 8: en bloque (3 niveles) en '

Al fallar cada una de estas pruebas, tenemos el marco que necesitamos para agregar migraciones, métodos, asociaciones y validaciones a nuestros modelos. A medida que nuestra aplicación evoluciona, los modelos se expanden y nuestro esquema cambia, este nivel de prueba nos brinda protección para introducir cambios de última hora..


Conclusión

Si bien no cubrimos demasiados temas en profundidad, ahora debería tener un conocimiento básico de la integración y las pruebas unitarias con Cucumber y RSpec. TDD / BDD es una de las cosas que los desarrolladores parecen hacer o no, pero he descubierto que los beneficios de TDD / BDD superan con creces los inconvenientes en más de una ocasión.