This code can be rails unit test with rspec? - ruby-on-rails

I'm studying rails and rspec.
And I made rspec unit test (request test) on rails application.
But after searching on google, I'm wonder if my job is on right way.
Can my code be a "Unit test by function(not a method, web site's feature ex)create, show, delete..) of rails application" ?
this is my code with request test.
require 'rails_helper'
RSpec.describe 'Users', type: :request do
let!(:users) { create_list(:user, 10) }
let(:user_id) { users.first.id }
let(:user) { create(:user) }
def send_request_to_store_user(name, mailaddress)
post '/users', params: {
user: {
name: users.first.name,
mailaddress: users.first.mailaddress
}
}
end
def http_status_success_and_body_element_check(body_element)
expect(response).to have_http_status(:success)
expect(response.body).to include(body_element)
end
describe 'GET' do
context 'Get /users test' do
it 'test user list page' do
get '/users'
http_status_success_and_body_element_check('User List')
end
end
context 'Get /users/create test' do
it 'test user create page' do
get '/users/create'
http_status_success_and_body_element_check('create user')
end
end
context 'Get /users/:id/edit' do
it 'test user edit page' do
get "/users/#{user_id}"
http_status_success_and_body_element_check('edit user')
end
end
context 'Get /users/:id' do
it 'test user show page' do
get "/users/#{user_id}"
http_status_success_and_body_element_check('show user')
end
end
end
describe 'POST' do
context 'test store new user' do
it 'test create new user' do
send_request_to_store_user(user.name, user.mailaddress)
expect do
create(:user)
end.to change { User.count }.from(User.count).to(User.count + 1)
end
it 'test redirect after create' do
send_request_to_store_user(user.name, user.mailaddress)
expect(response).to have_http_status(302)
end
end
end
describe 'DELETE' do
it 'test delete user' do
expect do
delete "/users/#{user_id}"
end.to change { User.count }.from(User.count).to(User.count - 1)
expect(response).to have_http_status(302)
end
end
describe 'PUT' do
context 'user update' do
it 'test user information update' do
old_name = users.first.name
new_name = 'new_name'
expect do
put "/users/#{user_id}", params: {
user: {
name: new_name
}
}
end.to change { users.first.reload.name }.from(old_name).to(new_name)
expect(response).to have_http_status(:redirect)
end
end
end
end
this is my code with test on model
require 'rails_helper'
RSpec.describe User, type: :model do
it 'user must have name and mailaddress' do
user = create(:user)
expect(user).to be_valid
expect(user.name).not_to be_nil
expect(user.mailaddress).not_to be_nil
end
it 'mailaddress must include #' do
# user = FactoryBot.create(:user)
# If rails_helper.rb has config.include FactoryBot::Syntax::Methods,
# Can use shortcut. Don't have to FactoryBot.create
user = create(:user)
# Test pass if email match with regexp
expect(user.mailaddress).to match(/\A[\w+\-.]+#[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/)
end
end

I don't think these tests are valuable (meaningful).
Here's my reasoning:
What are these tests telling you? That the Rails router is working? That the controller is responding with the right action? Neither of these are your responsibility to test. Rails has that covered.
If you want to know "does the index page render?" and "can I CRUD a user?" then write system tests with Capybara that simulate the whole flow. That way you are testing the real-world interaction with your whole system.

Related

RSpec test for ActiveAdmin member_action

I've got custom member_action in my Active Admin panel which is responsible for resending devise reset password instructions.
admin/users.rb
ActiveAdmin.register User do
member_action :reset_password do
user = User.find(params[:id])
user.send_reset_password_instructions
redirect_to(admin_user_path(user),
notice: "Password reset email sent to #{user.email}")
end
end
How to write RSpec tests for such an action? The only thing I found is this one and I think it's not quite related to my problem.
I was trying to sth like below:
require 'rails_helper'
describe Admin::UsersController, type: :controller do
include Devise::TestHelpers
let!(:admin) { create(:admin_user) }
before(:each) do
sign_in admin
end
describe 'GET user' do
let(:user) { create(:user, :random_email) }
before(:each) do
User.should_receive(:find).at_least(:once).and_return(user)
get :show
end
it 'sends email' do
get :reset_password
expect(user).should_receive(:send_reset_password_instructions)
end
end
end
But I'm getting an error:
ActionController::UrlGenerationError:
No route matches {:action=>"reset_password", :controller=>"admin/users"}
Personally I prefer to use a feature test, since when using active admin, UI stuff handle by the framework:
RSpec.feature 'Reset Password', type: :feature do
let(:user) { create :user }
before do
login_as(user, scope: :user)
end
scenario 'can delete future episode' do
visit some_path
click_link 'Reset Password'
expect(page.current_path).to eq(admin_user_path(user))
expect(page).to have_content("Password reset email sent to #{user.email}")
end
end
Ok, it turns out small adjustments (pass the user.id in params) make the trick.
describe Admin::UsersController, type: :controller do
include Devise::Test::ControllerHelpers
before { sign_in admin }
let!(:admin) { create(:admin_user) }
describe 'GET user' do
let(:user) { create(:user, :random_email) }
before do
allow(User).to receive(:find).at_least(:once) { user }
get :show, params: { id: user.id }
end
it 'sends email' do
get :reset_password, params: { id: user.id }
expect(flash[:notice]).to match("Password reset email sent to #{user.email}")
end
end
end

Why does the object built during the test disappear during the test (with RSPEC / Rails 5.2)?

I am testing the avaibility of BusinessArea views in the context of signed in / not signed in user.
At the beginning of the test, I create the business area object (test_ba) thanks to the factory, which returns the object.
I 'puts' the test_ba.id to see it created.
Then I request the tested view.
require 'rails_helper'
RSpec.describe BusinessArea, type: :request do
include Warden::Test::Helpers
describe "Business Areas pages: " do
test_ba = FactoryBot.create(:business_area)
puts test_ba.id
context "when not signed in " do
it "should propose to log in when requesting index" do
get business_areas_path
follow_redirect!
expect(response.body).to include('Sign in')
end
it "should propose to log in when requesting show" do
puts test_ba.id
get business_area_path(test_ba)
follow_redirect!
expect(response.body).to include('Sign in')
end
end
context "when signed in" do
before do
get "/users/sign_in"
test_user = FactoryBot.create(:user)
login_as test_user, scope: :user
end
it "should display index" do
get business_areas_path
expect(response).to render_template(:index)
end
it "should display business area" do
puts test_ba.id
get business_area_path(test_ba)
expect(response).to render_template(:show)
end
end
end
end
The test seems to run correctly, but the last step fails due to missing record!?! The ouput returns:
>rspec spec/requests/business_areas_spec.rb
67
.67
..67
F
Failures:
1) BusinessArea Business Areas pages: when signed in should display business area
Failure/Error: #business_area = BusinessArea.find(params[:id])
ActiveRecord::RecordNotFound:
Couldn't find BusinessArea with 'id'=67
# ./app/controllers/business_areas_controller.rb:159:in `set_business_area'
# ./spec/requests/business_areas_spec.rb:35:in `block (4 levels) in <top (required)>'
Finished in 2.07 seconds (files took 13.05 seconds to load)
4 examples, 1 failure
Failed examples:
rspec ./spec/requests/business_areas_spec.rb:33 # BusinessArea Business Areas pages: when signed in should display business area
Can you help me find what's wrong with this?
RSpec has the let and let! methods that create memoized helpers that you should use to setup your test dependency. let is lazy loading (the block is not evaluated until you reference it) while let! is not.
require 'rails_helper'
RSpec.describe BusinessArea, type: :request do
include Warden::Test::Helpers
describe "Business Areas pages: " do
let!(:test_ba){ FactoryBot.create(:business_area) }
context "when not signed in " do
it "should propose to log in when requesting index" do
get business_areas_path
follow_redirect!
expect(response.body).to include('Sign in')
end
it "should propose to log in when requesting show" do
puts test_ba.id
get business_area_path(test_ba)
follow_redirect!
expect(response.body).to include('Sign in')
end
end
context "when signed in" do
before do
get "/users/sign_in"
test_user = FactoryBot.create(:user)
login_as test_user, scope: :user
end
it "should display index" do
get business_areas_path
expect(response).to render_template(:index)
end
it "should display business area" do
puts test_ba.id
get business_area_path(test_ba)
expect(response).to render_template(:show)
end
end
end
end
But wah! Why doesn't my code work?
In RSpec (and in any good test framework) each example runs in isolation and has its own setup and teardown. This includes rolling back the database or clearing it. RSpec does not even run the tests in consecutive order by design.
The record you are defining in the outer context will not be created for each test run. After the first example when the db is is rolled back its gone.
If you want to set something up for each test use before:
require 'rails_helper'
RSpec.describe BusinessArea, type: :request do
include Warden::Test::Helpers
describe "Business Areas pages: " do
before do
#test_ba = FactoryBot.create(:user)
end
context "when not signed in " do
it "should propose to log in when requesting index" do
get business_areas_path
follow_redirect!
expect(response.body).to include('Sign in')
end
it "should propose to log in when requesting show" do
puts #test_ba.id
get business_area_path(test_ba)
follow_redirect!
expect(response.body).to include('Sign in')
end
end
context "when signed in" do
before do
get "/users/sign_in"
#test_user = FactoryBot.create(:user)
login_as test_user, scope: :user
end
it "should display index" do
get business_areas_path
expect(response).to render_template(:index)
end
it "should display business area" do
puts #test_ba.id
get business_area_path(test_ba)
expect(response).to render_template(:show)
end
end
end
end
But let / let! are preferred for setting up simple dependencies.

How to test activeadmin AuthorizationAdapter?

I have a custom AutorizationAdapter that I would like to test using RSpec:
class AdminAuthorization < ActiveAdmin::AuthorizationAdapter
def authorized?(_action, _subject = nil)
user.admin?
end
end
Initially I used a custom method but since I'm using Devise, using a custom AuthorizationAdapter seemed to be the way to go.
How would you go about testing it ? I tought one way to test it is to create a request spec for one of the controller and test for status code & redirection, something like that:
require 'rails_helper'
RSpec.describe 'AdminUsers', type: :request do
describe 'GET /admin_users' do
context 'admin' do
let(:admin_user) { create(:admin_user) }
before { sign_in super_user }
get admin_users_path
expect(response).to have_http_status(200)
end
context 'non admin' do
let(:user) { create(:user) }
before { sign_in user }
it 'redirects to the login page' do
get admin_users_path
expect(response).to have_http_status(302)
expect(response).to redirected_to '/admin/login'
end
end
context 'non logged in user' do
it 'redirects to the login page' do
get admin_users_path
expect(response).to have_http_status(302)
expect(response).to redirected_to '/admin/login'
end
end
end
end
I'm not sure this is the way to go.
These look reasonable to me. You can also look at the unit and feature specs that are in the ActiveAdmin test suite. However, AuthorizationAdapter itself is a PORO so you should be able to unit test in isolation: in the example given above that would be a fairly trivial test.

rspec doesn't run first test case

The block #update doesn't run. Why? How to change it to run all of them. #anything works fine.
describe UsersController, type: :controller do
login_admin
describe '#update' do
def user_update_params(roles:)
{
role_ids: roles.map(&:id),
name: 'new'
}
end
shared_examples_for 'update user' do
it 'change the user' do
expect do
put :update, id: user.id, user: user_params
end.to change { user.reload.name }
end
end
end
describe '#anything' do
it 'is ok' do
#runs ok
end
end
end
It's a shared example, not a real test. It is supposed to be included in other test groups. Like this:
describe '#whatever' do
it_behaves_like 'update user'
it 'runs shared example' do
end
end

RSpec - Functional Test - Syntax Check

so I'm writing a Test for my UserController, and the associated Devise dependency.
I'm trying to write a test that verify's userA can't access the show page of userB, but is redirected to the root_path instead. I'm guessing syntax errors are my issue, but I'd love another pair of eyes on it!
require 'rails_helper'
describe UsersController, :type => :controller do
# create test user
before do
#userA = User.create!(email: "test#example.com", password: "1234567890")
#userB = User.create!(email: "test2#example.com", password: "1234567890")
end
describe "GET #show" do
before do
sign_in(#userA)
end
context "Loads correct user details" do
get :show
expect(response).to have_http_status(200)
expect(assigns(:user)).to eq #userA
end
context "No user is logged in" do
it "redirects to login" do
get :show, id: #userA.id
expect(response).to redirect_to(root_path)
end
end
end
describe "GET Unauthorized page" do
before do
sign_in(#userA)
end
context "Attempt to access show page of UserB" do
it "redirects to login" do
get :show, id: #userB.id
expect(response).to have_http_status(401)
expect(response).to redirect_to(root_path)
end
end
end
end
You're missing an "it" block in
context "Loads correct user details" do
get :show
expect(response).to have_http_status(200)
expect(assigns(:user)).to eq #userA
end

Resources