Por defecto Ruby on Rails utiliza Minitest para crear pruebas automatizadas.
En la carpeta test
de cada proyecto encontrarás las siguientes carpetas y archivos:
controllers
- contiene las pruebas para los controladores.
fixtures
- define los datos iniciales para las pruebas.
helpers
- contiene las pruebas para los helpers.
integration
- contiene las pruebas de integración.
mailers
- contiene las pruebas de emails.
models
- contiene las pruebas de modelos.
test_helper.rb
- la configuración de Minitest.
Rails trae tres ambientes, cada uno con su base de datos independiente y su archivo de configuración independiente: development, test y production:
El archivo de configuración de test es: config/environments/test.rb
.
En el Gemfile, las gemas que estén en el grupo test solo están disponibles en ese ambiente.
En el archivo config/database.yml
se encuentra la configuración de la base de datos de pruebas bajo la llave test.
Al crear un modelo con rails g model
, se crean también:
Un archivo de prueba en la carpeta test/models
(p.e. article_test.rb
).
Un archivo para definir los datos iniciales en test/fixtures
(p.e. articles.yml
).
Lo principal que se debe probar en los modelos es:
Validaciones.
Scopes.
Métodos con lógica.
Veamos un ejemplo de pruebas de modelo:
require 'test_helper'class ArticleTest < ActiveSupport::TestCasetest "article is not created without a title" doarticle = Article.newassert_not article.saveendtest ".word_count returns the correct number of words" doarticle = Article.new(body: “Hola mundo. Esto es una prueba!”)assert_equal 6, article.word_countendtest "published scope only returns published articles" doarticles = Article.publishedassert_equal 1, articles.lengthassert_equal articles(:published).id, articles[0].idendend
Son archivos, escritos en formato YAML, que utiliza Rails para poblar la base de datos antes de ejecutar los tests. Existe un fixture por modelo.
Por ejemplo, si tenemos un modelo Author
, debemos agregar los fixtures en test/fixtures/authors.yml
:
pedro:email: [email protected]name: Pedro Perez
En este ejemplo estamos definiendo un registro que llamamos pedro
con valores para las columnas email
y name
.
Ahora, en las pruebas, nos podemos referir a ese registro como authors(:pedro)
. Por ejemplo:
test "pedro has the expected email" doassert_equal "[email protected]", authors(:pedro).emailend
Puedes agregar varios registros en un mismo archivo. Por ejemplo, si tenemos un modelo Article
podemos crear varios registros en test/fixtures/articles.yml
:
published:title: Prueba 1body: Cuerpo 1published: trueunpublished:title: Prueba 2body: Cuerpo 2published: false
En este caso nos podemos referir a cada registro de la siguiente forma:
articles(:published)articles(:unpublished)
En los fixtures puedes definir relaciones entre los modelos. Por ejemplo, si tenemos un modelo Article
que pertenece a un Author
, los podemos relacionar de la siguiente forma.
En el archivo authors.yml
:
pedro:email: [email protected]name: Pedro Perez
En el archivo articles.yml
podemos relacionar el autor utilizando el nombre del registro:
published:author: pedrotitle: Prueba 1body: Cuerpo 1published: true
Si la relación de Author
y Article
fuera una relación muchos a muchos se haría de la siguiente forma:
published:author: pedro, pablo, luistitle: Prueba 1body: Cuerpo 1published: true
La principal regla sobre los fixtures es nunca definir los id
s directamente, Rails se encarga de eso automáticamente.
Las pruebas de controlador se encuentran en la carpeta test/controllers
.
En este tipo de pruebas se simula una petición HTTP como si viniera del navegador.
Lo principal que se debe probar en los controladores es:
La seguridad. Que los usuarios sólo puedan ingresar a los recursos a los que tienen permisos.
Que cada acción del controlador retorne lo que esperamos.
Que las acciones de crear, editar y eliminar, creen editen y eliminen correctamente los registros.
Que las acciones de crear, editar y eliminar, no creen, editen o eliminen cuando la información que se les pasa no es valida.
Ejemplo que se encontraría en test/controllers/articles_controller_test.rb
:
require 'test_helper'class ArticlesControllerTest < ActionDispatch::IntegrationTesttest "get index: is successful" doget articles_pathassert_response :successendtest "post create: creates an article"assert_difference 'Article.count', 1 dopost articles_path, params: { article: { body: "El cuerpo", published: false, title: "El título" } }endassert_redirected_to article_url(Article.last)endend
La clase ActionDispatch::IntegrationTest
, de la cuál extienden las pruebas de controlador, exponen los métodos get
, post
, patch
, put
y delete
para hacer las pruebas de controladores.
Ejemplos:
get "/articles"get articles_pathget articles_path, params: { query: "batman" },headers: { "HTTP_AUTHORIZATION" => "..." }
Igual para los demás métodos.
Los siguientes son assertions adicionales que ofrece la clase ActionDispatch::IntegrationTest
, de la cuál extienden las pruebas de controlador:
assert_difference
: verifica que una expresión (p.e. Articles.count
) haya cambiado por una diferencia definida. Por ejemplo:
assert_difference 'Articles.count', 2 doArticle.create(title: "Título 1", body: "Cuerpo", published: true)Article.create(title: "Título 2", body: "Cuerpo", published: false)end
assert_response
: verifica que la respuesta a una petición HTTP tenga el código de respuesta esperado. Puedes pasarle el código especifico (p.e. 200) o alguna de las siguientes símbolos:
:success
- el código de respuesta está en el rango de 200 a 299 (exitoso).
:redirect
- el código de respuesta está en el rango de 300 a 399 (redirección).
:missing
- el código de respuesta fue 404.
:error
- el código de respuesta está en el rango de 500 a 599 (error del servidor).
assert_redirected_to
: verifica que la petición haya sido redireccionada a determinada ruta.
Existen dos formas que puedes utilizar para verificar que la vista contenga el contenido y los elementos que esperamos.
La primera forma es utilizar el método body
que trae, como una cadena de texto (string), todo el resultado. Por ejemplo:
get articles_pathassert body.include?("Título 1")
Una mejor forma es utilizar el método assert_select
que recibe un selector y, opcionalmente, un texto:
get articles_pathassert_select "table.articles"assert_select "h1", "Artículos"
Para configurar Devise en Minitest agrega lo siguiente al final del archivo test/test_helper.rb
:
class ActionDispatch::IntegrationTestinclude Devise::Test::IntegrationHelpersend
Ahora podemos utilizar el método sign_in
en nuestras pruebas. Por ejemplo, en test/controllers/articles_controller_test.rb
:
test "get index is successful if user is authenticated" dosign_in users(:pedro)get articles_pathassert_response :successend
El envío de emails se puede probar de forma aislada (pruebas unitaria) o dentro de nuestras otras pruebas (pruebas de integración).
El objetivo de las pruebas relacionadas con emails es asegurarse de que:
Los emails están son creados y enviados.
El contenido del email es el correcto (asunto, para, de, cuerpo, etc.).
Los emails correctos se están enviando en los momentos correctos.
Cuando creas un email utilizando el generador de Rails se crea un nuevo archivo en la carpeta test/mailers
. Por ejemplo para una clase UserMailer
vas a encontrar un archivo llamado user_mailer_test.rb
.
Veamos un ejemplo de lo podríamos probar en ese archivo:
require 'test_helper'class UserMailerTest < ActionMailer::TestCasetest "invite" do# Guardamos el email para futuros assertionsemail = UserMailer.create_invite('[email protected]','[email protected]', Time.now)# Enviar el email y verificar que fue encoladoassert_emails 1 doemail.deliver_nowend# Verificar que el contenido es el esperadoassert_equal ['[email protected]'], email.fromassert_equal ['[email protected]'], email.toassert_equal 'You have been invited by [email protected]', email.subjectassert_equal read_fixture('invite').join, email.body.to_send
Primero llamamos la clase UserMailer
para enviar el email. Luego verificamos si el email es enviado utilizando el assertion assert_emails
. Por último verificamos que el from
, to
, subject
y el body
sean los esperados.
Fíjate que la última línea de la prueba utiliza un método llamado read_fixture
. Esto se utiliza para guardar la respuesta que esperamos dentro de un archivo, que en este caso debería llamarse invite
y estar ubicado en la carpeta test/fixtures/user_mailer/
:
Hola [email protected],Has sido invitado.Saludos!
Cuando algún controlador o modelo envía emails, queremos verificar que efectivamente sean enviados. Para eso utiliza el método ActionMailer::Base.deliveries
, que retorna un arreglo de emails que se han enviado en la prueba:
require 'test_helper'class UserControllerTest < ActionDispatch::IntegrationTesttest "invite friend" doassert_difference 'ActionMailer::Base.deliveries.size', +1 dopost invite_friend_url, params: { email: '[email protected]' }endinvite_email = ActionMailer::Base.deliveries.lastassert_equal "Has sido invitado por [email protected]", invite_email.subjectassert_equal '[email protected]', invite_email.to[0]assert_match(/Hola [email protected]/, invite_email.body.to_s)endend
Las pruebas de aceptación son las más completas pero al mismo tiempo las más lentas.
Son lentas porque corren sobre un navegador. Por defecto se utiliza un emulador de un navegador, pero es posible ejecutarlas sobre Chrome o Firefox.
Las pruebas de aceptación se crean en la carpeta test/integration/
.
Para crear pruebas de integración se utiliza una gema llamada Capybara que trae métodos para manipular el navegador como visit
, click_link
y fill_in
. Por ejemplo:
test "user can register" dovisit root_pathclick_link "Registrarse"fill_in "Email", with: "[email protected]"fill_in "Contraseña", with: "test1234"click_button "Registrarse"assert_equal articles_path, current_pathend
La sugerencia es utilizar pruebas de aceptación para las funcionalidades críticas de la aplicación únicamente, y sólo el escenario normal en cada prueba.
Para configurar Capybara debes incluir la gema en el grupo test
de tu Gemfile
. Por ejemplo:
group :test dogem 'capybara'end
En el archivo test/test_helper.rb
debes agregar la siguiente línea después de los demás require
:
require 'capybara/rails'
También debes incluir la siguiente línea dentro de la clase ActionDispatch::IntegrationTest
:
include Capybara::DSL
Para navegar utiliza el método visit
:
visit "/articles"visit articles_path
Para hacer click sobre links o botones:
click_link "Texto del Link"click_link "id-del-link"click_button "Texto del Botón"click_button "id-del-botón"click_on "Guardar" # puede ser link o botón
Para interactuar con formularios:
fill_in 'First Name', with: 'John'check 'A checkbox'uncheck 'A checkbox'choose 'A radio button'select 'Option', from: 'Select box'unselect 'Option', from: 'Select box'attach_file 'Image', '/path/to/image.jpg'
Para verificar si existe el contenido o algún elemento:
has_content?("Hola mundo")has_no_content?("Hola mundo")has_css?(".mi-selector")has_no_css?(".mi-selector")
Para buscar elementos:
find("#mi-selector").clickfind_field("Nombre").valuefind_field(id: "name").value
Para limitar las acciones o las búsquedas:
within("#my-modal") dofill_in "Name", with: "Pedro"end