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
Related
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
I use an external site to handle my authentication. In my main controller, I do this by redirecting away to the external site which in turn redirects back to my site's callback url once the user authenticates.
I'm trying to stub this behavior in RSpec using Puffing Billy.
Here's my controller
class AuthController < ApplicationController
# `root_path` routes to here
def connect
some_external_url = "https://partner.co/some/path?foo=bar"
redirect_to(some_external_url)
end
# `auth_callback_path` routes to here
# The external site will also send over some data in the params,
# which will need to be stubbed as well in the specs below
def callback
data = params[:data]
binding.pry
# do stuff
end
end
My spec_helper has Webmock and Capybara configured as follows
# Set webmock and capybara drivers
WebMock.disable_net_connect!(allow_localhost: true)
Capybara.current_driver = :webkit_billy
Capybara.javascript_driver = :webkit_billy
And finally, the feature spec itself does the stubbing and testing
before(:each) do
# Stub the external url so that it redirects to the `callback` action above
proxy.stub("https://partner.co/some/path").and_return(Proc.new { |params|
data = params["foo"] == "bar" ? "good data" : "bad data"
url = auth_callback_url(data: data)
{ redirect_to: url }
})
end
it "should work" do
visit root_path
puts current_url #=> "https://partner.co/some/path?foo=bar"
end
The problem I'm seeing is that the redirect itself isn't working. After visiting the root path and getting redirected to the partner's site, it never redirects back to my app, even though I have that configured in the Proc object.
Could this be some interference from Webmock? Or did I misconfigure the stub?
Thanks!
EDIT:
After various iterations of testing above, decided to simplify and see if using the proxy was working as expected when visiting some plain old static page.
Ran the blow code snippet:
# spec/spec_helper.rb
RSpec.configure do |config|
config.before(:each, :use_puffing_billy) do
Capybara.default_driver = :webkit_billy
Capybara.javascript_driver = :webkit_billy
end
end
# spec/feature/my_spec.rb
it "can do stuff", :use_puffing_billy, js: true do
#
# `/contact` is a standard "Contact Us" static page. It requires no
# authentication, has no redirects, etc... making it useful for simply
# testing whether the page loads or not
#
# Capybara.app_host => nil
# Billy.config.proxy_host => "localhost"
visit contact_url
binding.pry
#
# current_url => "http://www.example.com:51984/contact"
# page.body.present? => false
visit contact_path
binding.pry
#
# current_url => "http://127.0.0.1:51984/contact"
# page.body.present? => true
end
It appears that current_url does not work but current_path does, likely because they use different hosts.
With rails 4.2.0 and the latest version of RSpec I generated a controller test.
How would I ensure my admin user is logged in?
For example: if current_user.admin?
In the rspec test it mentions it like so:
let(:valid_session) { {} }
How would I enter a valid session?
First you need to add the devise helpers in spec_helper file to be accessible in the tests, as mentioned in the wiki
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
Then in the controller you could easily create a user object and sign it in using sign_in helper method
Step 1:
You can create custom methods like following in spec folder and then simply use them (after you have done what #Mohammad AbuShady's answer states which usually is done by default in rspec)
module ControllerMacros
def login_admin
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:admin]
sign_in FactoryBot.create(:user, admin:true)
end
end
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in FactoryBot.create(:user)
end
end
end
Step 2:
Add login_user or login_admin to your spec file wherever you need and change
let(:valid_session) { {} }
to
let(:valid_session) { {"warden.user.user.key" => session["warden.user.user.key"]} }
I hope you are using devise and warden which are really useful if you don't want to worry about session/login/signup issues.
You can see their documentations here:
plataformatec/devise
wardencommunity/warden
This answer was written based on documentation of devise:
devise-wiki-how-to-test
I want to test my multidomain RoR3 App.
Here's my test_helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'capybara/rails'
require 'blueprints'
class ActiveSupport::TestCase
end
class ActionDispatch::IntegrationTest
include Capybara
def host
"http://#{subdomain}.lvh.me:3000"
end
def subdomain
#subdomain ? #subdomain : 'demostore'
end
def visit(url)
super("http://#{subdomain}.lvh.me:3000#{url}")
end
end
And my integration test:
require 'test_helper'
class ProductsTest < ActionDispatch::IntegrationTest
def setup
#subdomain = 'demostore'
# creating stuff
end
def teardown
# deleting stuff
end
test "user views product list" do
visit('/')
assert page.has_css?('ul.product-listing')
assert page.has_xpath?("//ul[#class='product-listing']/li", :count => 12)
end
test "user views product page" do
product = Product.first
visit('/')
find(:xpath, "//ul[#class='product-listing']/li/a[1]").click
save_and_open_page
end
end
And I'm sure the link exists. There is problem with clicking and filling stuff.
click_link('Existent link title')
doesn't work too.
I think the default Capybara's driver Rack::Test could have problems with this multidomain stuff?
In your setup, call this rack::test function, which will change your host's value. Well, it changes the host that gets returned about the fake web request.
host! "#{store.subdomain}.example.com"
The problem was that i'm using multidomain stuff so I had to use lvh.me which resolves localhost. You can do the same think by setting in Your /etc/hosts
127.0.0.1 subdomain.yourapp.local
and then use this domain.
I've overwritten Capybara's visit method with sth like that:
def visit(link)
super("mysubdomain.lvh.me:3000#{link}")
end
but problem persisted because when Capybara clicked for example link, the visit method was not used and my host was not requested. Which was? I don't know - probably the default one.
So solution is to set host and port in Capybara settings:
class ActionDispatch::IntegrationTest
include Capybara
Capybara.default_host = "subdomain.yourapp.local"
Capybara.server_port = 3000
# ... rest of stuff here
end
Apparently it's a problem with rack-test.
But there is a fork of it by hassox that just solved it for me.
It's just a couple of commits that really matter, in case you want to check what the changes are.
This is how my Gemfile looks:
group :test, :cucumber do
gem 'rack-test', :git => "https://github.com/hassox/rack-test.git"
gem 'capybara', '= 0.4.1.2'
gem 'capybara-envjs', '= 0.4.0'
gem 'cucumber-rails', '>= 0.3.2'
gem 'pickle', '>= 0.3.4'
end
And then I just make sure to
visit('http://my_subdomain.example.com')
in my steps. Now I'm trying to understand what would make url helpers work with subdomains.
Here's a quick setup that may help you out...
rails 3.2+ testing custom subdomains using cucumber capybara with pow setup:
https://gist.github.com/4465773
I'd like to share what I found to be a great solution for this problem. It involves creating a helper method to prepend URLs with the desired subdomain, doesn't overwrite any Capybara methods, and works with the Rack::Test and capybara-webkit drivers. In fact, it will even work in specs which do not even use Capybara. (source: http://minimul.com/capybara-and-subdomains.html)
The Spec Helper Method
# spec/support/misc.helpers.rb
def hosted_domain(options = {})
path = options[:path] || "/" # use root path by default
subdomain = options[:subdomain] || 'www'
if example.metadata[:js]
port = Capybara.current_session.driver.server_port
url = "http://#{ subdomain }.lvh.me:#{ port }#{ path }"
else
url = "http://#{ subdomain }.example.com#{ path }"
end
end
And to illustrate it's use, here are two examples:
Used in a Feature Spec (with Capybara)
require 'spec_helper'
describe "Accounts" do
# Creates an account using a factory which sequences
# account subdomain names
# Additionally creates users associated with the account
# using FactoryGirl's after callbacks (see FactoryGir docs)
let (:account) { FactoryGirl.create(:account_with_users) })
it "allows users to sign in" do
visit hosted_domain(path: new_sessions_path, subdomain: account.subdomain)
user = account.users.first
fill_in "email", with: user.email
fill_in "password", with: user.password
click_button "commit"
# ... the rest of your specs
end
end
Used in a Request Spec (without Capybara)
#spec/requests/account_management_spec.rb
require "spec_helper"
describe "Account management" do
# creates an account using a factory which sequences
# account subdomain names
let (:account) { FactoryGirl.create(:account) })
it "shows the login page" do
get hosted_domain(path: "/login", subdomain: account.subdomain)
expect(response).to render_template("sessions/new")
end
end
A simple and clean solution is to override the urls you provide to Capybara's visit method. It works well with *.lvh.me domains, which will redirect you to localhost:
describe "Something" do
def with_subdomain(link)
"http://subdomain.lvh.me:3000#{link}"
end
it "should do something" do
visit with_subdomain(some_path)
end
end
Or you could do the same by redefining app_host before a spec:
Capybara.app_host = 'http://sudbomain.lvh.me:3000'
..
visit(some_path)
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