Si has tenido la enorme suerte de trabajar en un proyecto en el que se usa Rails de la manera tradicional (dónde las páginas HTML se renderizan en el servidor y la carga de Javascript es la mínima indispensable), aquí tienes una breve guía de cómo debes testear tu aplicación para conseguir una cobertura y velocidad de ejecución óptimas.
Todas las aplicaciones que realicemos deberán implementar los siguientes tests (siempre que sea posible):
Es la única parte que no cambia respecto a lo que ya veníamos haciendo. Deberemos testear de forma unitaria los modelos, los jobs, las policies y las clases propias usando la forma habitual.
- Evita persistir los modelos en la base de datos salvo que sea necesario. Si usas
FactoryBotpara generar modelos, intenta utilizarbuilden lugar decreatesiempre que te sea posible.
Con la llegada de Rails 5.1 se mejoró todo el proceso de testeo y el Core Team de Rails decidió introducir un nuevo tipo de test: los tests del sistema (system tests). Estos tests están pensados para testear la interacción del usuario a través del navegador.
Estos tests testean desde el principio al final un determinado flujo dentro de nuestra aplicación, verificando que todos los componentes de nuestra página funcionan correctamente. No debemos testear todos los casos ni los casos raros, ya que este tipo de tests son más costosos en tiempo de ejecución que los demás.
Básicamente es lo mismo que ya teníamos desde hace años en RSpec con los tests de features usando Capybara pero con la principal diferencia de que el driver que controla el navegador se ejecuta en el mismo thread que el servidor de Rails, por lo que la conexión a la base de datos se comparte y por tanto se elimina la necesidad de usar gemas como DatabaseCleaner y la necesidad de borrar e inicializar la base de datos en cada prueba, permitiendo el uso de transacciones y acelerando con ello la velocidad de ejecución de los tests.
Adicionalmente estos tests ya vienen preconfigurados para detectar y usar Capybara, por lo que no tendremos que realizar configuración adicional alguna para que funcione out of the box.
Estos tests se definen en la ruta spec/system tal y como se explica en la documentación oficial.
Aunque sean mas veloces que los tradicionales, siempre testearemos con estos tests los flujos principales de la aplicación, delegando en los tests de requests todas las comprobaciones de permisos o flujos especiales.
Cuando ejecutamos nuestros tests para simular la interacción del usuario, Capybara debe usar lo que se conoce como driver, que es lo que le permite comunicarse con un entorno de ejecución que se encargará de renderizar el contenido de nuestra aplicación y realizar las acciones que nosotros le indiquemos.
Por defecto Capybara usa un driver llamado :rack_test que permite interactuar con el HTML de nuestra página para aplicar selectores y comprobar el contenido de los elementos y que se ejecuta de manera muy rápida (ya que no usa un navegador externo). A cambio este driver no puede ejecutar Javascript, por lo que sólo lo podremos usar en aquellas páginas en las que no tengamos que verificar ninguna interacción que requiera Javascript.
En cambio, si queremos realizar comprobaciones que necesitan ejecutar código Javascript, deberemos usar un driver que se ejecute en un navegador. Para ello Capybara dispone del driver :selenium que implementa un protocolo para abrir y realizar las acciones sobre un navegador, ya sea Google Chrome o Firefox.
Como ejemplo, esta podría ser una configuración de RSpec para ejecutar nuestros tests de sistema usando diferentes drivers en función de lo que vamos a testear necesita ejecutar código Javascript o no:
RSpec.configure do |config|
config.before(:each, type: :system) do
driven_by :rack_test
end
config.before(:each, type: :system, js: true) do
driven_by :selenium, using: :headless_chrome, screen_size: [1920, 1080] do |driver_option|
driver_option.add_argument('--no-sandbox')
end
end
config.define_derived_metadata(file_path: /spec\/system/) do |metadata|
metadata[:browser] = true
end
config.filter_run_excluding browser: true # to execute system tests must run rspec -t browser
end-
Siempre que podamos evitaremos el uso de
itespecíficos para cada prueba y usaremos en su lugar un únicoitque abarque los máximos flujos posibles (principales o de error). -
Evitaremos iniciar sesión mediante la interfaz (formulario de login), usando
stubsen el controlador para que devuelva automáticamente el usuario autenticado.
Los tests de tipo request nos permiten testear nuestra aplicación pasando por todo el stack de una petición, incluyendo el router, los middleware de rack y nuestro controlador, siendo mucho más realista que el testeo individual de cada controlador como ocurría en los test de controlador. Debido a la mayor cobertura del código, es la nueva forma preferida.
Usaremos estos tests para comprobar, por cada controlador:
- El código de respuesta HTTP.
- El contenido de la página es correcto.
- Si se aplica alguna redirección o no.
Ya no es necesario comprobar las variables de isntancia asignadas o si se renderiza una vista concreta, ya que lo que nos interesa es el resultado de la página renderizada.
Para comprobar el contenido de la página podemos aplicar dos técnicas:
- Usar los matchers de Capybara como
have_content, que aunque no vienen incluídos por defecto en los tests de tiporequest, podemos incluirlos nosotros. Es la forma preferida por disponer de más formas de comprobar el contenido y hacer uso de selectores CSS.
# spec/support/capybara_matchers.rb
RSpec.configure do |config|
config.include Capybara::RSpecMatchers, type: :request
end# spec/requests/dashboard_spec.rb
it { expect(response.body).to have_content('Lorem ipsum') }- Usar el matcher include para comprobar el contenido en el test.
# spec/requests/dashboard_spec.rb
it { expect(response.body).to include('Lorem ipsum') }Una colección de enlaces donde puedes profundizar más en el cambio en la forma de testear en Rails: