Saltar al contenido

BDD con Ruby on Rails usando RSpec y Capybara

Por qué los tests de controladores muchas veces prueban lo que Rails ya garantiza, y cómo los feature tests con RSpec y Capybara permiten testear lo que el usuario realmente ve.

6 min de lectura
  • ruby
  • rails
  • testing
  • bdd

Publicado originalmente en Medium

La mayoría de mis posts han tratado sobre testing en Ruby on Rails, generalmente sobre tests unitarios y funcionales. Pero esta vez tocaré un tema un poco más complejo, un tema por el que tuve crisis existenciales al hacer testing, un tema del que quizás no sepas absolutamente nada pero que, terminando de leer el post, les aseguro que desearán saber más y más.

Y sí… ha llegado el momento de hablar sobre BDD.

Introducción

Desarrollo guiado por comportamiento, o Behavior-driven development en inglés, es un nuevo enfoque al TDD que nació años atrás para desarrollar tests basados en los 'features' de nuestra aplicación.

Algunos dicen que BDD reemplaza a TDD, otros que BDD es lo mismo que TDD, otros que BDD es algo completamente diferente a TDD… [el ver tantas T y D es algo curioso, ¿no creen? ( ಠ_ಠ )]

Como sea, lo cierto es que BDD tiene un punto claro y específico: los tests deben ser lo más cercanos al lenguaje natural del ser humano; pondré un ejemplo más adelante para explicar a qué me refiero con esto.

TDD, dentro de Ruby on Rails, suele usar tests unitarios para los modelos y tests funcionales para los controladores; sin embargo, muchas veces se cae en el error de testear algo demasiado obvio o, peor aún, testear funcionalidades que por defecto hace el sistema.

BDD sigue los mismos pasos de TDD, tests-first, pero para crear los tests se basa en otro enfoque: prueba lo que ves. La idea de BDD es testear cosas que tú ves en la aplicación, testear los resultados finales luego de realizar ciertas acciones, probar específicamente que la aplicación hace lo que debe hacer y no engañarte con simples tests unitarios que, como lo dije anteriormente, muchas veces no hacen más que testear funcionalidades que por defecto el sistema ya realiza.

BDD everywhere…

Al momento de usar TDD en RoR, en la mayoría de casos hago tests unitarios para los modelos y tests funcionales para los controladores. Dentro de mis tests para mis controladores, uno de los specs más comunes es:

expect(response).to render_template 'template_name'

Luego de un tiempo trabajando con TDD, surgió una pregunta dentro de mí que hacía dudar mi existencia: ¿por qué testear la funcionalidad que por defecto Rails ya realiza?

Me refiero a que un controlador de por sí ya hace render de una vista. Por ejemplo, dentro de un PostsController una acción INDEX hace render de la vista index, una acción SHOW hace render de la vista show; es decir, Rails ya nos asegura que la acción hará un render de su respectiva vista a menos que nosotros especifiquemos otra cosa. Pero si no hacemos ninguna modificación en el render de la vista… ¿por qué testearlo?

¿Por qué testear algo que por defecto ya hace el sistema? ¿Es realmente necesario crear tests para estos casos? ¿Por qué? ¿Para qué?…

Buscando una respuesta en el vasto mundo de Internet, me encontré con una pregunta parecida a la mía en StackOverflow; bueno, no era parecida a la mía, recuerdo que era una pregunta referida a cómo se deberían testear justamente los renders de las vistas.

Revisando entre las respuestas encontré una que logró resolver mi pregunta planteada anteriormente. La respuesta comenzaba explicando cómo hacer los specs en RSpec haciendo uso del 'render_template' y más, pero al final daba una recomendación: este gran señor explicó que no es necesario hacer un test para el render de las vistas, y que lo que se debería hacer es crear feature tests o tests de alto nivel para probar la funcionalidad final de la aplicación; hacer uso de BDD para navegar en la aplicación y comprobar que los datos se muestran correctamente.

Pongamos un ejemplo sencillo: tengo mi PostsController con las acciones INDEX y SHOW. Lo que haría en TDD normalmente sería:

# INDEX action
it 'show the correct view' do
  expect(response).to render_template 'index'
end
 
# SHOW action
it 'show the correct view' do
  expect(response).to render_template 'show'
end

y con los dos specs, cada uno en su respectivo método, seríamos felices.

Pero con BDD nosotros no testeamos que una acción muestre la vista que le corresponde; nosotros deberíamos testear la parte final, lo que realmente queremos que haga la aplicación. Déjeme explicarle esto con un feature spec usando RSpec y Capybara para nuestra acción SHOW:

feature 'Show the post selected' do
  scenario 'user want to see the first post' do
    visit posts_path
    click_link 'Its my first posts'
    expect(page).to have_content 'Its my first post!' # Titulo del post mostrado en la web app
    expect(page).to have_content 'Welcome to my first post guys' # body del post
  end
end

En Capybara un feature es igual a un describe y un scenario igual a un it.

Expliquemos un poco el código:

  • Línea 3: el usuario visita la url de todos los posts.
  • Línea 4: el usuario hace click en el link del primer post, que tiene por nombre 'Its my first post'.
  • Línea 5: le indicamos a nuestro test que la página debería tener como texto el título del primer post: 'Its my first posts', que bien podría ser un h1, h2 o incluso un p (párrafo).
  • Línea 6: le indicamos a nuestro test que la página también debería tener el texto del body del post, que en este caso es: 'Welcome to my first…'.

Lo que hacemos arriba es testear que todos los datos del post se muestren cuando alguien ingrese a la url respectiva. Esto lo logramos gracias a Capybara, que nos permite navegar por la aplicación vía interfaces externas —que para nuestro caso es una web—; en otras palabras: podemos simular el comportamiento real que un usuario tendría con nuestra aplicación.

Lo primero que notamos es que ya no necesitamos testear que se haga el render de la vista correcta, ya que nuestro feature test prueba eso y mucho más. Prueba que:

  • Se muestre la url de forma correcta de mi primer post.
  • La url realmente funciona.
  • La url realmente me redirige al post que quiero.
  • La página a la que me redirige luego de hacer click en el link del primer post realmente muestra el título y cuerpo de este.

¿Lo ves? ¡Tenemos un test mucho más poderoso!, que, por decirlo así, tiene tests embebidos dentro de él.

Analicemos un poco más el feature test que tenemos:

feature 'Show the post selected' do
  scenario 'user want to see the first post' do
    visit posts_path
    click_link 'Its my first posts'
    expect(page).to have_content 'Its my first post!'
    expect(page).to have_content 'Welcome to my first post guys'
  end
end

¿Puedes darte cuenta de la verbosidad usada en el test? ¡La sintaxis es muy pero muy cercana al lenguaje humano! Y es que este es otro de los puntos claves de BDD: hacer que tus tests se lean y entiendan de forma fácil, que incluso un no-desarrollador pueda entenderlos.

¿Te preguntas con qué fin quieres que tus tests sean fáciles de entender y leer? Pues el que lo sean hace posible eliminar posibles problemas que puede traer TDD… ¿cómo lo hace?… La capacidad de leer tus pruebas como simples oraciones influirá en cómo piensas acerca de tus pruebas; el punto es que si usted las entiende, entonces usted podría ser capaz de escribir mejores y más completos tests.

Aún queda mucho por abordar, como las pruebas de aceptación, las pruebas visuales, cómo generar reportes basados en tus tests, el porqué elegir entre Cucumber y Capybara y mucho más; lo dejaré aquí para no hacer el post más largo de lo que es. Pero no se preocupen, pienso hacer otro post para tocar los temas que faltan.

Prueba tus funcionalidades, no las inherentes al sistema…

Finalmente

BDD hace que te centres en el comportamiento del usuario, el cual obviamente está basado en las funcionalidades de tu aplicación, escribiendo tests más entendibles y fáciles de leer con el fin de tener mejores tests.

Con esto no quiero decir que dejemos de usar TDD, sino que dejemos de testear características que por default nuestro framework favorito ya hace, ¡empezando a crear pruebas más completas y poderosas! ʕ•ܫ•ʔ

¡Gracias por leer! Si el post le gustó, compártalo con sus conocidos o gente a la que le pueda servir; después de todo, ese es mi objetivo: tratar de ayudar a las personas.

Por último, le dejo otros de mis posts relacionados al testing con Ruby on Rails (◕‿◕):