Para testar as models associadas ao Devise, vamos usar como exemplo a model User:
rails g devise User name:string
Lembre-se de fazer a migração:
rails db:migrate
Se você fez a instalação do rspec e do factory-bot corretamente como descrita nos passos e , então na pasta rspec/factories deve ter sido criado o arquivo users.rb e na pasta rspec/models criado o arquivo user_spec.rb.
Com isso, fazemos como nas outras models, com o , para criar um default User:
FactoryBot.define do
factory :user do
name { 'teste' }
email { 'email@teste.com' }
password { '12345678' }
password_confirmation { '12345678' }
end
end
Observe que precisamos colocar um email e senha que são obrigatórias para uma model do Devise.
Feito isso, podemos começar a testar a model no user_spec.rb como foi feito anteriormente:
RSpec.describe User, type: :model do
describe 'factory' do
context 'when using standard factory' do
it { expect(build(:user)).to be_valid }
end
end
describe 'validations' do
context 'when user does not have an email' do
it { expect(build(:user, email: nil)).not_to be_valid }
end
end
end
A partir disso, podemos adicionar outras validações que dependerão das validations da model User(no arquivo app/models/user.rb), como presence do name:
validates :name, presence: true
Dessa forma:
RSpec.describe User, type: :model do
describe 'factory' do
context 'when using standard factory' do
it { expect(build(:user)).to be_valid }
end
end
describe 'validations' do
context 'when user does not have an email' do
it { expect(build(:user, email: nil)).not_to be_valid }
end
context 'when user does not have a name' do
it { expect(build(:user, name: nil)).not_to be_valid }
end
end
end
Testando controllers que exigem autenticação
Para testarmos controllers, será necessário adicionar no arquivo rails_helper.rb criado automaticamente na própria pasta rspec as seguintes linhas dentro do bloco RSpec.configure do |config|:
Assim, o bloco RSpec.configure do |config| deve ficar mais ou menos assim:
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# You can uncomment this line to turn off ActiveRecord support entirely.
# config.use_active_record = false
# RSpec Rails can automatically mix in different behaviours to your tests
# based on their file location, for example enabling you to call `get` and
# `post` in specs under `spec/controllers`.
#
# You can disable this behaviour by removing the line below, and instead
# explicitly tag your specs with their type, e.g.:
#
# RSpec.describe UsersController, type: :controller do
# # ...
# end
#
# The different available types are documented in the features, such as in
# https://relishapp.com/rspec/rspec-rails/docs
config.infer_spec_type_from_file_location!
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
# Devise Test helpers
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::IntegrationHelpers, type: :request
end
Agora podemos começar a montar os testes das nossas controllers.
Suponha que na sua aplicação o usuário(user) tenha produtos favoritos que ele quer adicionar para poupar tempo. Dessa forma, na controller de favoritos, você pode querer colocar uma função para ver favoritos e outra para criar favoritos:
class Api::V1::FavouriteController < ApplicationController
acts_as_token_authentication_handler_for User
def index
favourites = current_user.favourites
render json: favourites, status: :ok
end
def create
favourite = Favourite.new(favourites_params)
if current_user.id === favourite.user_id then
favourite.save!
render json: favourite, status: :created
elsif favourite.user_id === nil then
render json: {message: "unprocessable_entity" }, status: :unprocessable_entity
else
render json: { message: "Você não pode criar um favorito para outro usuário" }, status: :unauthorized
end
rescue StandardError => e
render json: {message: e.message}, status: :unprocessable_entity
end
end
Como iríamos testar esses métodos se eles requerem o user estar logado? A resposta é simples: precisamos criar o user e usar o token e o email dele nos headers quando chamar o get/post:
describe "/GET #index" do
let(:user) { create(:user) }
let(:product) { create(:product, name: 'product1') }
let(:product2) { create(:product, name: 'product2')}
before do
create(:favourite, user_id: user.id, product_id: product.id)
create(:favourite, user_id: user.id, product_id: product2.id)
end
context 'logged in as user'
before do
get '/api/v1/favourites/', headers: {
'X-User-Token': user.authentication_token,
'X-User-Email': user.email
}
end
it { expect(response).to have_http_status(:ok) }
it 'returns with json' do
expect(response.content_type).to eq('application/json; charset=utf-8')
end
it 'returns 2 elements' do
expect(JSON.parse(response.body).size).to eq(2)
end
end
E a mesma coisa faríamos para testar o método post:
...
let(:user) { create(:user) }
let(:product) { create(:product) }
let(:params) do {
product_id: product.id,
user_id: user.id
}
end
context 'logged in as user with valid params' do
before do
post "/api/v1/favourites/create", params: { favourite: params }, headers: {
'X-User-Token': user.authentication_token,
'X-User-Email': user.email
}
end
it { expect(response).to have_http_status(:created) }
it 'creates the favourite' do
new_favourite = Favourite.find_by(user_id: user.id, product_id: product.id)
expect(new_favourite).not_to be_nil
end
end
...
E quando o user não está logado, como testamos a falha na autenticação?
context 'not logged in as user' do
before do
post "/api/v1/favourites/create", params: { favourite: params }
end
it 'returns a failure response' do
expect(response).to redirect_to authentication_failure_path
end
end
Como mostrado, usamos o authentication_failure_path que criamos na . Dessa forma, como no post não foram informados o token e o email do user, a resposta foi redirecionada para o authentication_failure_path e no teste conseguimos testar isso também.