i've started writing tests for my controllers. For once of them, i need to be logged in, in order to check whether the page is rendered etc.
Since i'm using Devise that has a current_user helper over a User model, how can i form my :
describe "when it GETS 'town'" do
it "should render the template and be successful" do
get :index
response.should render_template(:index)
response.should be_success
end
end
so that i don't get the undefined "authenticate!" method (devise specific) error ?
There's a good writeup on this topic on the Devise wiki. Basically you need to add the Devise test helpers, then call sign_in to get a valid current user when your spec expects one.
Related
my rspec file ->
describe 'GET /users' do
it 'test user list page' do
get '/users'
expect(response).to have_http_status(:success)
end
end
I maked login in my app, but I don't know how to define
'login' with 'before' in rspec file.
my login is using session, and I don't have to use specific user.
how can I check user logged in on rspec request test?
I want to test logged in user can access to /users url in rspec file
you can do it by mocking. The details depend on how your login status is determined, but suppose your application controller has a before_action like this:
# application_controller.rb
before_action :verify_login
# typically the checking is delegated
def verify_login
Authorize.logged_in?(current_user, session)
end
Where the checking is executed in the Authorize service model.
Now in Rspec you just have to mock the response:
before do
current_user = FactoryBot.create(:user)
session = Session.create
expect(Authorize).to receive(:logged_in?).with(current_user, session).and_return(true)
end
it "should respond to logged-in user" do
get '/users'
expect(response).to have_http_status(:success)(
end
I'm trying to test some Rails controller actions that require a login. Right now logging in is managed by setting the logged in user to session[:user_id] which all works as expected, but when I try to manually set it in my test (definitely open to a better way!) I get NoMethodError: undefined method session' for nil:NilClass` which is a really confusing error. message in the first place--I would have expected a NameError for an undefined variable. I'd like to test both logged in and non logged in methods so I'm not sure if logging in during the setup phase is accurate (although I couldn't get it working anyway) but I'm open to any and all suggestions. This is for a lesson teaching testing, so I want to keep it basic and please point out if I'm doing anything super wrong here before I teach it to impressionable youth.
require 'test_helper'
class TacosControllerTest < ActionDispatch::IntegrationTest
setup do
#meat = tacos(:meat)
#veggie = tacos(:veggie)
end
test "should redirect if not logged in" do
get new_taco_url
assert_redirected_to login_path
end
test "should get new if logged in" do
session[:user_id] = users(:pat).id
get new_taco_url
assert_response :success
end
test "should create new taco" do
session[:user_id] = users(:pat).id
assert_difference("Taco.count") do
post tacos_url, params: {taco: {name: "Yummy", price: 2.50}}
end
end
end
In my rails app I have two user roles: 'student' and 'admin'. They have different access authorization to different pages, e.g., 'admin' can access listing users page (index) but 'student' cannot. This is controlled using cancancan.
Now I am writing tests for controllers and, since I have two different roles, (to my knowledge) I need two separate tests for a single behaviour, for example:
test "should get index for admin" do
sign_in(users(:admin))
get "index"
assert_response :success
end
test "should not get index for student" do
sign_in(users(:student))
get "index"
assert_response :unauthorized
end
where sign_in(*) is the method for handing user login (sessions etc.)
As I am considering adding more roles (e.g. 'manager', 'agent'), I need to add new tests for all the controller methods each time I add a role. This is tedious and not "DRY", so I am trying to find a more elegant way to handle this. Here is my first thought:
In my test_helper.rb, I added:
def assert_admin_only(&block)
sign_in(users(:admin))
instance_exec(&block)
assert_response :success
sign_out
sign_in(users(:student))
instance_exec(&block)
assert_response :unauthorized
sign_out
sign_in(users(:agent))
instance_exec(&block)
assert_response :unauthorized
sign_out
end
Then in my test:
test "should get index for admin only" do
assert_admin_only do
get "index"
end
end
So that each time I added a new role, I only have to add a few lines in the test_helper.rb method in order to test the abilities.
However, it does not work as I thought because "Functional tests allow you to test a single controller action per test method." according to Rails API DOC, while in my code I was firing two actions or even more. For some reason I can't figure out, it seems that the sign_in and sign_out don't actually change the current_user (although they are working perfectly in real requests), which essentially make my attempt fail.
In a word, I want to reuse my tests for different user roles so that I don't have to waste my time copying and pasting existing codes every time I add a new role. I would really appreciate it if you guys could provide some brilliant ideas.
for example:
require 'test_helper'
class ArticlesControllerTest < ActionController::TestCase
include Devise::TestHelpers
setup do
#article = articles(:one)
#admin = users(:admin)
#expert = users(:expert)
#user = users(:emelya)
#student = users(:student)
end
test "should get index if admin" do
sign_in #admin
ability = Ability.new(#admin)
assert ability.can? :index, Article
get :index
assert_response :success
assert_not_nil assigns(:articles)
end
test "should not get index for other users" do
[#expert, #user, #student] do |user|
sign_in user
ability = Ability.new(user)
assert ability.cannot? :index, Article
assert_raise CanCan::AccessDenied do
get :index
end
sign_out user
end
end
end
I'm currently new to RSpec and trying to implement some Controller testing with RSpec
In my Rails app, I'm using Devise as my authentication system. My question is, When we test a controller which uses some authentication system (in my case Devise), what is the standard practice?
Is it
1 - to skip the authentication
or
2 - to authenticate the controller
as per the question, following is my controller
require File.dirname(__FILE__) + '/../spec_helper'
describe ProjectsController do
include Devise::TestHelpers
p "starting..."
before(:each) do
p "in before method"
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in Factory.create(:user)
end
it "should create a project" do
p "should create a project"
end
after(:each) do
#user.destroy unless #user.nil?
end
end
I can only see 'starting', But why its not going to "in before method" and "should create a project"
I'm using Rspec2 and Rails2 on Ubuntu.
Check this: Stubbing Devise in rSpec and Rails3.
Standard practice is not skipping authentication, but effectively making sure that a correct user is logged in (for devise).
Referring to your code: have you tried to create some real test? E.g. something as simple as
it "gets index" do
get :index
response.status.should be == 200
end
I am not sure why you are not seeing the print-statements. Either rspec skips the empty step (there is no real code), or because something else went wrong. But honestly, I am not even sure if using p inside rspec works.
A tool like rubymine allows you to easily debug your specs if you want to step into it (which imho is a better approach then the scattered p statements).
Does anyone know how to make rspec follow a redirect (in a controller spec)? (e.g test/unit has follow_redirect!)
I have tried "follow_redirect!" and "follow_redirect" but only get
undefined method `follow_redirect!' for #<Spec::Rails::Example::ControllerExampleGroup::Subclass_1:0xb6df5294>
For example:
When I create an account the page is redirected to accounts page and my new account should be at the top of the list.
it "should create an account" do
post :create, :name => "My New Account"
FOLLOW_REDIRECT!
response.code.should == "200"
accounts = assigns[:accounts]
accounts[0].name.should == "My New Account"
end
But FOLLOW_REDIRECT! needs to be changed to something that actually works.
I think this is the default behavior for rspec-rails controller tests, in the sense that you can set an expectation on the response status and/or path, and test for success.
For example:
it "should create an account" do
post :create
response.code.should == "302"
response.should redirect_to(accounts_path)
end
You can access the redirect location with
response.headers['Location']
you could then request that directly.
If you want to test the redirect you are moving outside of the rspec-rails domain.
You can use Webrat or some other integration-test framework to test this.
The easiest way to solve this without resorting to integration testing is probably to mock out the method that is causing the redirect.
The spec is out of scope, if you want to follow a redirect use request spec, the equivalent of integration test in Test::Unit.
In request specs follow_redirect! works as well as in Test::Unit.
Or if you want to redirect inmediately use _via_redirect as suffix for the verb, example:
post_via_redirect :create, user: #user
Try to use integration/request tests. They are using web-like acces through routing to controllers.
For example:
I have for Rails 2 app in file /spec/integration/fps_spec.rb
require 'spec_helper'
describe "FinPoradci" do
it "POST /fps.html with params" do
fp_params={:accord_id => "FP99998", :under_acc => "OM001", :first_name => "Pavel", :last_name => "Novy"}
fp_test=FinPoradce.new(fp_params)
#after create follow redirection to show
post_via_redirect "/fps", {:fp => fp_params}
response.response_code.should == 200 # => :found , not 302 :created
new_fp=assigns(:fp)
new_fp.should_not be_empty
new_fp.errors.should be_empty
flash[:error].should be_empty
flash[:notice].should_not be_empty
response.should render_template(:show)
end
end
and it works. Until you want to send headers (for basic http authorization).
env={'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user,password)}
post_via_redirect "/fps", {:fp => fp_params}, env
is fine for create, but after redirection it returns 401 and needs new authorization.
SO I have to split it in 2 tests: creation and show on result of creation.
For RSpec / Capybara + Rails
response_headers['Location']
But it works only if there is no delay before redirect.
If it is there, then it's harder to follow the logic.