How do I stub current_user in Rails 5?
I expected this:
#controller.stub :current_user, mock_user do
end
to work, but there is no #controller in ActionDispatch::IntegrationTest? I use sorcery gem for authentication.
You could try the wiki example:
class TeamsControllerTest < ActionController::TestCase
include Sorcery::TestHelpers::Rails::Integration
include Sorcery::TestHelpers::Rails::Controller
setup do
#team = teams(:one)
#user = users(:one)
login_user(user = #user, route = login_url) # replace with your login url path
end
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:teams)
end
That will set your test user as current_user.
I am using paperclip-dropbox gem to store my assets. I want to create a test to ensure assets are properly destroyed if the destroy checkbox is checked.
I can see on dropbox the image is uploaded and deleted but just after it's created again. Don't understand why.
Here is my test file:
require 'test_helper'
#
# == Admin namespace
#
module Admin
#
# == SettingsController test
#
class SettingsControllerTest < ActionController::TestCase
include Devise::TestHelpers
setup :initialize_test
#
# == Avatar
#
test 'should be able to upload logo' do
upload_dropbox_paperclip_attachment
setting = assigns(:setting)
assert setting.logo?
assert_equal 'bart.png', setting.logo_file_name
assert_equal 'image/png', setting.logo_content_type
end
test 'should be able to destroy logo' do
upload_dropbox_paperclip_attachment
remove_dropbox_paperclip_attachment
end
private
def initialize_test
#setting = settings(:one)
#administrator = users(:bob)
sign_in #administrator
end
def upload_dropbox_paperclip_attachment
puts '=== Uploading logo to Dropbox'
attachment = fixture_file_upload 'images/bart.png', 'image/png'
patch :update, id: #setting, setting: { logo: attachment }
end
def remove_dropbox_paperclip_attachment
puts '=== Removing logo from Dropbox'
patch :update, id: #setting, setting: { logo: nil, delete_logo: '1' }
assert_not assigns(:setting).logo?
end
end
end
Do someone know what is wrong with this test ?
Am I in the right way trying to test for dropbox or should I test only in local ? the documentation for paperclip-dropbox doesn't give any information about testing.
Thanks
My project:
Rails 4.2.3
Ruby 2.2.2
I'm writing tests with rspec for my application controller in my rails app (written in Rails 4) and I'm running into a problem where it doesn't recognize the route for the HTTP request I'm sending. I know there's a way to do this using MyApp::Application.routes but I'm not able to get it working.
#application_controller_spec.rb
require 'spec_helper'
class TestController < ApplicationController
def index; end
end
describe TestController do
before(:each) do
#first_user = FactoryGirl.create(:user)
# this is to ensure that all before_filters are run
controller.stub(:first_time_user)
controller.stub(:current_user)
end
describe 'first_time_user' do
before(:each) do
controller.unstub(:first_time_user)
end
context 'is in db' do
before(:each) do
#user = FactoryGirl.create(:user)
controller.stub(:current_user).and_return(#user)
end
it 'should not redirect' do
get :index
response.should_not be_redirect
end
end
context 'is not in db' do
context 'session[:cas_user] does not exist' do
it 'should return nil' do
get :index
expect(assigns(:current_user)).to eq(nil)
end
end
it "should redirect_to new_user_path" do
controller.stub(:current_user, redirect: true).and_return(nil)
get :index
response.should be_redirect
end
end
end
The error I'm getting right now is
No route matches {:action=>"index", :controller=>"test"}
I would add the test#index route to config/routes.rb, but it doesn't recognize the Test Controller, so I want to do something like
MyApp::Application.routes.append do
controller :test do
get 'test/index' => :index
end
end
but I'm not sure where to add this or if this even works in rspec. Any help would be great!
If you are trying to test your ApplicationController, see this RSpec documentation about it. You will need to define methods like index inside the test, but it works well.
Here my http basic authentication in the application controller file (application_controller.rb)
before_filter :authenticate
protected
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == "username" && password == "password"
end
end
and the default test for the index action of my home controller (spec/controllers/home_controller_spec.rb)
require 'spec_helper'
describe HomeController do
describe "GET 'index'" do
it "should be successful" do
get 'index'
response.should be_success
end
end
Test doesn't run because of the authentication method. I could comment "before_filter :authenticate" to run them but I would like to know if there is way to make them worked with the method.
Thank you!
Update (2013): Matt Connolly has provided a GIST which also works for request and controller specs: http://gist.github.com/4158961
Another way of doing this if you have many tests to run and don't want to include it everytime (DRYer code):
Create a /spec/support/auth_helper.rb file:
module AuthHelper
def http_login
user = 'username'
pw = 'password'
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user,pw)
end
end
In your test spec file:
describe HomeController do
render_views
# login to http basic auth
include AuthHelper
before(:each) do
http_login
end
describe "GET 'index'" do
it "should be successful" do
get 'index'
response.should be_success
end
end
end
Credit here - Archived site
Sorry I didn't seek enough, the solution seems to be the following:
describe "GET 'index'" do
it "should be successful" do
#request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("username:password")
get 'index'
response.should be_success
end
end
Some answers suggest to set request.env which is unsafe, because request can be nil and you will end up with private method env' called for nil:NilClass, especially when run single tests with rspec -e
Correct approach will be:
def http_login
user = 'user'
password = 'passw'
{
HTTP_AUTHORIZATION: ActionController::HttpAuthentication::Basic.encode_credentials(user,password)
}
end
get 'index', nil, http_login
post 'index', {data: 'post-data'}, http_login
For me, with Rails 6, I need keyword arguments for rspec get method like .. get route, params: params, headers: headers
Auth Helper method
module AuthHelper
def headers(options = {})
user = ENV['BASIC_AUTH_USER']
pw = ENV['BASIC_AUTH_PASSWORD']
{ HTTP_AUTHORIZATION: ActionController::HttpAuthentication::Basic.encode_credentials(user,pw) }
end
def auth_get(route, params = {})
get route, params: params, headers: headers
end
end
and the rspec request test.
describe HomeController, type: :request do
include AuthHelper
describe "GET 'index'" do
it "should be successful" do
auth_get 'index'
expect(response).to be_successful
end
end
end
When using Rspec to test Grape APIs, the following syntax works
post :create, {:entry => valid_attributes}, valid_session
where valid_session is
{'HTTP_AUTHORIZATION' => credentials}
and
credentials = ActionController::HttpAuthentication::Token.encode_credentials("test_access1")
These are great solutions for controller and request specs.
For feature tests using Capybara, here is a solution to make HTTP Basic authentication work:
spec/support/when_authenticated.rb
RSpec.shared_context 'When authenticated' do
background do
authenticate
end
def authenticate
if page.driver.browser.respond_to?(:authorize)
# When headless
page.driver.browser.authorize(username, password)
else
# When javascript test
visit "http://#{username}:#{password}##{host}:#{port}/"
end
end
def username
# Your value here. Replace with string or config location
Rails.application.secrets.http_auth_username
end
def password
# Your value here. Replace with string or config location
Rails.application.secrets.http_auth_password
end
def host
Capybara.current_session.server.host
end
def port
Capybara.current_session.server.port
end
end
Then, in your spec:
feature 'User does something' do
include_context 'When authenticated'
# test examples
end
My solution:
stub_request(method, url).with(
headers: { 'Authorization' => /Basic */ }
).to_return(
status: status, body: 'stubbed response', headers: {}
)
Use gem webmock
you can tighten verification by change:
/Basic */ -> "Basic #{Base64.strict_encode64([user,pass].join(':')).chomp}"
URL - can be a regular expression
I have written a functional test that changes some of the request object's environment variables to simulate a user has logged in.
require 'test_helper'
class BeesControllerTest < ActionController::TestCase
# See that the index page gets called correctly.
def test_get_index
#request.env['HTTPS'] = "on"
#request.env['SERVER_NAME'] = "sandbox.example.com"
#request.env['REMOTE_USER'] = "joeuser" # Authn/Authz done via REMOTE_USER
get :index
assert_response :success
assert_not_nil(assigns(:bees))
assert_select "title", "Bees and Honey"
end
end
The functional test works fine.
Now I want to do something similar as part of integration testing. Here is what I tried:
require 'test_helper'
class CreateBeeTest < ActionController::IntegrationTest
fixtures :bees
def test_create
#request.env['HTTPS'] = "on"
#request.env['SERVER_NAME'] = "sandbox.example.com"
#request.env['REMOTE_USER'] = "joeuser" # Authn/Authz done via REMOTE_USER
https?
get "/"
assert_response :success
[... more ...]
end
end
I get an error complaining that #request is nil. I suspect this has something to do with the session object, but I am not sure how to get it to work.
You can set HTTPS in integration tests with
https!
And set the host name with:
host! "sandbox.example.com"
Which may be equivalent to what you want to do?
This is described in the Rails guides Rails guides
You can change request variables via parameters to post method.
For your case, method test_create will be:
def test_create
https!
get "/", nil, { 'SERVER_NAME'] => "sandbox.example.com", 'REMOTE_USER'] => "joeuser" }
assert_response :success
[... more ...]
end
Same works for setting post request to raw data:
post root_path, nil, { 'RAW_POST_DATA' => 'some string' }