Simulating a non-local request in Rails/RSpec request testing - ruby-on-rails

I'd like to block off access to the application to all non-local requesters (my application's actual functionality in practice is more sophisticated, but figuring out how to do this will solve my specific issue). How would I go about testing this with request tests in RSpec?
In spec/requests/gatekeeper_spec.rb
describe "A local request to the site root" do
before :each do
get root_path
end
it "should not allow access" do
response.status.should be(401)
end
end
describe "An external (terminology?) request to the site root" do
before :all do
# TODO: make request remote
end
before :each do
get root_path
end
it "should allow access" do
response.status.should be(200)
end
end
How should I implement the # TODO line? I've looked into mocks and think that rigging request.remote_ip may be appropriate, but I'm not certain exactly how such a mock is implemented.

If I understand correctly, test requests have a remote address of "0.0.0.0", so they would normally be considered remote and you'd want to stub the local requests, not the other way around.
I think this should work for controller specs -- not sure about request specs:
request.stub(:local?) { true }

Untested, but should work in Rails 2.3.x and 3.0:
before :each do
Rails::Initializer.run do |config|
config.action_controller.consider_all_requests_local = false
end
end
after :each do
Rails::Initializer.run do |config|
config.action_controller.consider_all_requests_local = true
end
end

In Rails 4 you can do it with:
RSpec.configure do |config|
config.before(:each, allow_rescue: true) do
Rails.application.config.action_dispatch.stub(:show_exceptions) { true }
Rails.application.config.stub(:consider_all_requests_local) { false }
end
end
And then in your test file:
describe "A test" do
it "renders custom error pages", :allow_rescue => true do
# ...
end
end
The name :allow_rescue is taken from a ActionController::Base.allow_rescue configuration which exists in Rails 3, and there the RSpec configuration would be:
RSpec.configure do |config|
config.before(:each, allow_rescue: true) do
ActionController::Base.stub(:allow_rescue) { true }
end
end

Related

Is it possible to disable ActiveAdmin authorization only for the tests?

I'm going to add some request tests for the formal testing ActiveAdmin page. Is it possible to disable ActiveAdmin authorization for these tests?
I need something like this for the test configuration:
config.authentication_method = false
config.current_user_method = false
Never did it before but extending rspec DSL might work
For instance put this module to support folder
module ActiveAdminSpecHelper
def without_active_admin_auth
authentication_method = ActiveAdmin.application.authentication_method
current_user_method = ActiveAdmin.application.current_user_method
yield
ensure
ActiveAdmin.application.authentication_method = authentication_method
ActiveAdmin.application.current_user_method = current_user_method
end
end
and then
RSpec.configure do |config|
require 'support/active_admin_spec_helper'
config.include ActiveAdminSpecHelper
end
After this u will be able to use it like
it "does something" do
without_active_admin_auth do
expect(something).to works_fine
end
end
Looks like the easiest way is to include stubbed authorization with Devise::Test::ControllerHelpers and write the rest as usual
# spec/controllers/admin/countries_spec.rb
require 'rails_helper'
describe Admin::CountriesController do
include Devise::Test::ControllerHelpers
render_views
before { sign_in create(:admin_user) }
describe 'GET #index' do
it 'renders countries' do
get :index
expect(response).to have_http_status(:ok)
end
end
end

RSpec controller testing http://test.host/login error on index spec test

I am creating controller tests for my application. I am just trying to test the index action for my maps_controller.
I created this test for the index to see if it renders.
require 'rails_helper'
RSpec.describe MapsController, type: :controller do
describe 'GET #index' do
it 'shows a list of all maps' do
get :index
expect(response).to redirect_to maps_path
end
end
end
However when I run this spec test I get this error message.
Failure/Error: expect(response).to redirect_to maps_path
Expected response to be a redirect to http://test.host/maps but was a redirect to http://test.host/login.
Expected "http://test.host/maps" to be === "http://test.host/login".
I can understand this because it does require a user to get to this path. However, I am unsure why this is trying to go to http://test.host/maps instead of localhost:3000/maps. I was unable to find anything in RSpec docs about setting the test url. How can I set this to go to localhost:3000 instead of test.host?
You can set it globally in your spec_helper.rb file:
Since your spec is set to type: :controller, you would do it this way:
RSpec.configure do |config|
# other config options
config.before(:each) do
#request.host = "localhost:3000"
end
end
However, as of RSpec 3.5 the preferred method of testing controllers is with a :request spec. So if possible, change your spec to type: :request and then you would do it this way:
RSpec.configure do |config|
# other config options
config.before(:each) do
host! "localhost:3000"
end
end
its work for me
RSpec.configure do |config|
config.before(type: :request) do
host! "localhost:3000"
end
end
set default hosts in environment configuration:
config/environments/test.rb
config.action_mailer.default_url_options = { host: 'http://localhost' }
config.action_controller.default_url_options = { host: 'http://localhost' }
also, you probably want set default host for request, then you can override default value in rails_helper.rb like:
module ActionDispatch
class TestRequest
def host
'localhost'
end
end
end
That should be enought.

Rails 4 rspec 3 controller test: session helper module not working for before(:all), works for before(:each)

I'm building a toy chat application using Rails 4.2.7, and am writing specs for my controllers using rspec 3.5. My Api::ChatroomsController requires a user to be logged in in order to create a chatroom, so I have created a Api::SessionsHelper module to create sessions from within the Api::ChatroomsController spec.
# app/helpers/api/sessions_helper.rb
module Api::SessionsHelper
def current_user
User.find_by_session_token(session[:session_token])
end
def create_session(user)
session[:session_token] = user.reset_session_token!
end
def destroy_session(user)
current_user.try(:reset_session_token!)
session[:session_token] = nil
end
end
# spec/controllers/api/chatrooms_controller_spec.rb
require 'rails_helper'
include Api::SessionsHelper
RSpec.describe Api::ChatroomsController, type: :controller do
before(:all) do
DatabaseCleaner.clean
User.create!({username: "test_user", password: "asdfasdf"})
end
user = User.find_by_username("test_user")
context "with valid params" do
done = false
# doesn't work if using a before(:all) hook
before(:each) do
until done do
create_session(user)
post :create, chatroom: { name: "chatroom 1" }
done = true
end
end
let(:chatroom) { Chatroom.find_by({name: "chatroom 1"}) }
let(:chatroom_member) { ChatroomMember.find_by({user_id: user.id, chatroom_id: chatroom.id}) }
it "responds with a successful status code" do
expect(response).to have_http_status(200)
end
it "creates a chatroom in the database" do
expect(chatroom).not_to eq(nil)
end
it "adds the chatroom creator to the ChatroomMember table" do
expect(chatroom_member).not_to eq(nil)
end
end
end
I'm using a before(:each) hook with a boolean variable done to achieve the behavior of a before(:all) hook for creating a single session.
If I use a before(:all) hook, I get the error:
NoMethodError: undefined method `session' for nil:NilClass`
I put a debugger in the create_session method of the Api::SessionsHelper module to check self.class and in both cases, when I use before(:each) and when I use before(:all), the class is:
RSpec::ExampleGroups::ApiChatroomsController::WithValidParams
However when using the before(:each) hook, session is {}, while in the before(:all) hook, session gives the NoMethodError above.
Anybody know what causes this error?
You need to include the helper in the test block:
RSpec.describe Api::ChatroomsController, type: :controller do
include Api::SessionsHelper
end
You can also avoid duplication by including common spec helpers in spec/rails_helper.rb
RSpec.configure do |config|
# ...
config.include Api::SessionsHelper, type: :controller
end
This is also where you should put the database_cleaner config. You should use to clean between every spec not just before all as that will lead to test ordering issues and flapping tests.
require 'capybara/rspec'
#...
RSpec.configure do |config|
config.include Api::SessionsHelper, type: :controller
config.use_transactional_fixtures = false
config.before(:suite) do
if config.use_transactional_fixtures?
raise(<<-MSG)
Delete line `config.use_transactional_fixtures = true` from rails_helper.rb
(or set it to false) to prevent uncommitted transactions being used in
JavaScript-dependent specs.
During testing, the app-under-test that the browser driver connects to
uses a different database connection to the database connection used by
the spec. The app's database connection would not be able to access
uncommitted transaction data setup over the spec's database connection.
MSG
end
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, type: :feature) do
# :rack_test driver's Rack app under test shares database connection
# with the specs, so continue to use transaction strategy for speed.
driver_shares_db_connection_with_specs = Capybara.current_driver == :rack_test
if !driver_shares_db_connection_with_specs
# Driver is probably for an external browser with an app
# under test that does *not* share a database connection with the
# specs, so use truncation strategy.
DatabaseCleaner.strategy = :truncation
end
end
config.before(:each) do
DatabaseCleaner.start
end
config.append_after(:each) do
DatabaseCleaner.clean
end
end

Rails/RSpec toggle cache for a single test

So in my app I can disable the cache for all tests, which would be ideal, but apparently there are a number of legacy tests that rely on the cache being functional. Is there a way to enable the Rails cache for a single RSpec test?
Something like:
before(:each) do
#cache_setting = Rails.cache.null_cache
Rails.cache.null_cache = true
end
after(:each) do
Rails.cache.null_cache = #cache_setting
end
it 'does not hit the cache' do
...
end
in spec_helper.rb
RSpec.configure do |config|
config.before(:example, disable_cache: true) do
allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::NullStore.new)
end
config.after(:example, disable_cache: true) do
allow(Rails).to receive(:cache).and_call_original
end
end
in xxx_spec.rb
RSpec.describe "a group without matching metadata" do
it "does not run the hook" do
puts Rails.cache.class
end
it "runs the hook for a single example with matching metadata", disable_cache: true do
puts Rails.cache.class
end
end
https://www.relishapp.com/rspec/rspec-core/docs/hooks/filters

Rails rspec set subdomain

I am using rSpec for testing my application. In my application controller I have a method like so:
def set_current_account
#current_account ||= Account.find_by_subdomain(request.subdomains.first)
end
Is it possible to set the request.subdomain in my spec? Maybe in the before block? I am new to rSpec so any advice on this would be great thanks.
Eef
I figured out how to sort this issue.
In my before block in my specs I simply added:
before(:each) do
#request.host = "#{mock_subdomain}.example.com"
end
This setups up the request.subdomains.first to be the value of the mock_subdomain.
Hope someone finds this useful as its not explained very well anywhere else on the net.
I know this is a relatively old question, but I've found that this depends on what kind of test you're running. I'm also running Rails 4 and RSpec 3.2, so I'm sure some things have changed since this question was asked.
Request Specs
before { host! "#{mock_subdomain}.example.com" }
Feature Specs with Capybara
before { Capybara.default_host = "http://#{mock_subdomain}.example.com" }
after { Capybara.default_host = "http://www.example.com" }
I usually create modules in spec/support that look something like this:
# spec/support/feature_subdomain_helpers.rb
module FeatureSubdomainHelpers
# Sets Capybara to use a given subdomain.
def within_subdomain(subdomain)
before { Capybara.default_host = "http://#{subdomain}.example.com" }
after { Capybara.default_host = "http://www.example.com" }
yield
end
end
# spec/support/request_subdomain_helpers.rb
module RequestSubdomainHelpers
# Sets host to use a given subdomain.
def within_subdomain(subdomain)
before { host! "#{subdomain}.example.com" }
after { host! "www.example.com" }
yield
end
end
Include in spec/rails_helper.rb:
RSpec.configure do |config|
# ...
# Extensions
config.extend FeatureSubdomainHelpers, type: :feature
config.extend RequestSubdomainHelpers, type: :request
end
Then you can call within your spec like so:
feature 'Admin signs in' do
given!(:admin) { FactoryGirl.create(:user, :admin) }
within_subdomain :admin do
scenario 'with valid credentials' do
# ...
end
scenario 'with invalid password' do
# ...
end
end
end
In rails 3 everything I tried to manually set the host didn't work, but looking the code I noticed how nicely they parsed the path you pass to the request helpers like get.
Sure enough if your controller goes and fetches the user mentioned in the subdomain and stores it as #king_of_the_castle
it "fetches the user of the subomain" do
get "http://#{mock_subdomain}.example.com/rest_of_the_path"
assigns[:king_of_the_castle].should eql(User.find_by_name mock_subdomain)
end
Rspec - 3.6.0
Capybara - 2.15.1
Chris Peters' answer worked for me for Request specs, but for Feature specs, I had to make the following changes:
rails_helper:
Capybara.app_host = 'http://lvh.me'
Capybara.always_include_port = true
feature_subdomain_helpers:
module FeatureSubdomainHelpers
def within_subdomain(subdomain)
before { Capybara.app_host = "http://#{subdomain}.lvh.me" }
after { Capybara.app_host = "http://lvh.me" }
yield
end
end

Resources