I want to test a simple sign in flow, which needs a existed user in the test database.
describe 'Signin page' do
before :each do
User.generate(:email => 'automatic_tester#gmail.com', :password => 'palmdrive', :first_name => 'Automatic', :last_name => 'Tester')
end
it 'signs in a user', :js => true do
signin
current_path.should == redirect_path
end
end
def signin
## Action to sign in the user
end
## Use DatabaseCleaner since selenium driver is used
DatabaseCleaner.strategy = :truncation
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before :each do
DatabaseCleaner.start
end
config.after :each do
DatabaseCleaner.clean
end
end
Because the way to create a user is a little bit more complicated, so I defined the User.generate method to create a user. To ensure it actually works, in the rails console with test environment, running User.generate(:email => 'automatic_tester#gmail.com', :password => 'palmdrive', :first_name => 'Automatic', :last_name => 'Tester')it successfully created a user in database. Comment out the before :each, the test suite passed successfully.
But the problem is running the codes above, the test fails. It was due to the user can't be created in the database. Why can't the User.generate method create a user?
In the spec_helper add:
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || retrieve_connection
end
end
# Forces all threads to share the same connection. This works on
# Capybara because it starts the web server in a thread.
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
solves the issue. Then no need to use the DatabaseCleaner either
Use create instead
User.create(:....)
Note that create will create a record in your database. Writing to the DB is an expensive operation and you may want to avoid that in some cases by using new rather than create.
A better approach is to use factories. See FactoryGirl https://github.com/thoughtbot/factory_girl
Related
If you have succeeded in testing post, put, and delete http methods of a Rails API protected with the doorkeeper OAuth2 provider gem, please share and I'll give you the love.
The doorkeeper wiki documentation and sample application show pretty well how to test a get method. I succeeded testing a post with something like what follows using the Capybara test driver with Cucumber. Failed to test any API that routes from put or delete. Failed to post using an rspec test.
#user = create :user
#client = create(:oauth_application)
#token = create(:oauth_token, :application => #client, :resource_owner_id => #user)
json_for_new_entry = {
date_attr: Time.now.to_date,
decimal_attr: '1.1',
string_attr: 'oath2, you make me blue',
bool_attr: false,
int_attr: 1
}.to_json
page.driver.header 'Authorization', "Bearer #{#token.token}"
page.driver.post api_entry_path, json_for_new_entry,
'CONTENT_TYPE' => 'application/json'
The factories are nothing special:
factory :user, :class => User do |user|
sequence :email do |n| "user#{n}#example.com" end
pwd = "password"
password pwd
end
factory :oauth_application, :class => Doorkeeper::Application do
sequence(:name) { |n| "application_name_#{n}" }
#redirect_uri 'urn:ietf:wg:oauth:2.0:oob'
redirect_uri 'http://localhost:3000/'
end
factory :oauth_token, :class => Doorkeeper::AccessToken do
association :application, :factory => :oauth_application
association :resource_owner_id, :factory => :user
end
My environment is a little behind latest versions:
rails gems at 3.1.12
capybara 2.2.0
cucumber 1.3.10
devise 2.2.7
warden 1.2.3
doorkeeper 0.7.4
rspec-core 2.14.5
rspec-expectations 2.14.3
rspec-mocks 2.14.3
rspec-rails 2.14.0
Assuming the intention of your test is to verify the underlying API functionality and not the doorkeeper protection then this is the hack I use:
In my base controller:
module Api
class BaseController < ActionController::Base
doorkeeper_for :all unless Rails.env.test?
private
def current_user
if Rails.env.test? && $test_user
$test_user
else
#current_user ||= User.find(doorkeeper_token.resource_owner_id)
end
end
end
end
In my tests I have a login helper:
def login(user)
$test_user = user
end
def logout
$test_user = nil
end
I'm not proud of that code but nonetheless I can now get on with my life instead of worrying about how to make rails/doorkeeper/capybara et al work together during testing.
You can use the example included in doorkeeper wiki as follow
describe Api::V1::ProfilesController do
describe 'GET #index' do
let(:token) { double :acceptable? => true }
before do
controller.stub(:doorkeeper_token) { token }
# allow(controller).to receive(:doorkeeper_token) {token} # => RSpec 3
end
it 'responds with 200' do
get :index, :format => :json
response.status.should eq(200)
end
end
end
I used the answer that Moustafa gave, but I wanted to DRY it up so I put the following into spec/support/doorkeeper_oauth.rb:
shared_context "doorkeeper_oauth", oauth: true do
let(:dummy_token) { double(:acceptable? => true) }
before do
if controller.present?
allow(controller).to receive(:doorkeeper_token) { dummy_token }
end
end
Then, in your controller spec you change the opening line slightly:
describe Api::V2::WidgetsController, oauth: true do
which pulls in the shared context via the "metadata" method.
edit: I have used this for at least GET and POST, which success in both cases.
I am using Rspec, FactoryGirl and Spork for my tests.There are 2 things I am a litte unclear on, first is the location of my factories.rb file. At present I have it located in
spec/support/factories.rb
And it looks like this
FactoryGirl.define do
factory :user do
email "example#yahoo.com"
password "password"
password_confirmation "password"
confirmed_at Time.now
end
end
Within my spec_helper I have
config.include FactoryGirl::Syntax::Methods
Secondly I want to login a user before starting my tests for a controller , this particular controller has a before filter :authenticate_user!
I am using devise for my authentication so have added
config.include Devise::TestHelpers, :type => :controller
Reading the devise docs you can add a controller_macros.rb and specify methods like so to use
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
sign_in user
end
end
And so i added this also to my spec_helper
config.include ControllerMacros, :type => :controller
So when I add login_user before my controller tests i get undefined method login_user. Am i using two tools here to do the same thing? Do I actually need the devise methods or can it all be done with factoryGirl. If so how do i setup the login process before i can test a controller?
Factories location should be in spec/factories. Check out this example app https://github.com/RailsApps/rails3-devise-rspec-cucumber/tree/master/spec.
For login, generally you seems to doing it right. Check the example app again and here: https://github.com/plataformatec/devise/wiki/How-To:-Controllers-and-Views-tests-with-Rails-3-%28and-rspec%29
For the undefined method login_user error be sure to have
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
and
config.extend ControllerMacros, :type => :controller
in spec_helper. Devise methods should be available wtih subject:
subject.current_user.should_not be_nil
I have a running Rails application, using ActiveAdmin and its models to autenticate users. Now I'm interested in moving to an ActiveDirectory authentication, so my users can validate wiht the domain's users.
I've been trying adauth and it looks like a great gem, but I'm a little bit lost when trying to "mix" this gem with my ActiveAdmin authentication. I'm pretty sure I'm not the first one in doing it, so any help would be appreciated.
Thanks!
I finally was able to manage to integrate AD in ActiveAdmin.
Here's what I did, in case someone is interested:
Include gem 'adauth' in your gems
Execute bundle install
Execute rails g adauth:config
Configure the config/initializers/adauth.rb for your AD connection. For example, if your domain is example.com, you must include:
c.domain = "example.com"
c.server = "IP address of your domain controller"
c.base = "dc=example, dc=com"
Execute rails g adauth:sessions
Modify your application_controller.rb. Mine was:
class ApplicationController< ActionController::Base
protect_from_forgery
helper_method :current_user
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def authenticate_user!
if current_user.nil?
redirect_to '/sessions/new', :error => "Invalid Login"
end
end
end
Execute rails g adauth:user_model user install_adauth.
This creates the migration install_adauth, but for some reason it was empty. I had to fill it myself with:
class InstallAdauth < ActiveRecord::Migration
def up
create_table :users do |u|
u.string 'login'
u.text 'group_strings'
u.string 'name'
u.string 'ou_strings'
end
end
def down
drop_table :users
end
end
Execute rake db:migrate
Modify your sessions_controller.rb. Mine was:
class SessionsController < ApplicationController
def new
redirect_to '/admin' if current_user
end
def create
ldap_user = Adauth.authenticate(params[:username], params[:password])
if ldap_user
user = User.return_and_create_with_adauth(ldap_user)
session[:user_id] = user.id
redirect_to '/admin'
else
redirect_to '/sessions/new', :error => "Invalid Login"
end
end
def destroy
session[:user_id] = nil
redirect_to '/sessions/new'
end
end
So far the validation through ActiveAdmin still works. To switch to ActiveDirectory we must change the file initializers/active_admin.rb
# config.authentication_method = :authenticate_admin_user!
config.authentication_method = :authenticate_user!
#config.current_user_method = :current_admin_user
config.current_user_method = :current_user
In my case, I needed to restart Apache too.
If anytime we want to switch back to ActiveAdmin, we just need to undo the last change
I added some confirmation dialog boxes for my Rails 3.1 application and, prior to that, their corresponding tests. Following the model of Railscast #257, I added ':js => true' to the test, added database_cleaner and modified the spec_helper.rb file accordingly.
When I run the test, Firefox launches, Capybara-Selenium fills in the fields the the appropriate username and a password, but log-in fails (i.e., "invalid username/password".) Other tests that do not have ':js => true' and also login, do still pass.
I would like to add more javascript to my application in the future and I am avoiding solutions that would hack Capybara to get this to work (e.g., click 'OK' on all dialogs.)
Any ideas what I might be missing? Fail that, any suggestions on how to debug this problem?
Thank you.
You should set use_transactional_fixtures = false for your seleniumtests.
This can be done in the spec_helper.rb with
config.use_transactional_fixtures = false
for all your tests.
Or do this for a single testcase:
describe 'testcase' , :type => :request do
self.use_transactional_fixtures = false
it 'your test', :js => :true do
testing...
end
end
This happens because selenium-tests access the database in a different way. With transactional fixtures enabled, selenium will work on an empty database -> your user does not exist and you cannot login.
For normal tests you should use transactional fixtures because your tests run much faster.
As per Avdi Grimm's post (features a detailed explanation):
Gemfile:
group :test do
gem 'database_cleaner'
end
spec_helper.rb:
config.use_transactional_fixtures = false
spec/support/database_cleaner.rb:
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, :js => true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
I had transactional fixtures disabled but adding the :js => true block to database_cleaner.rb did it for me.
I saw various versions of how to setup a global HTTP_REFERER in RSpec, but none of them worked with RSpec 2.6.4:
RSpec.configure do |config|
config.before(:each, :type => :controller) do
request.env["HTTP_REFERER"] = root_url
end
end
The request is always nil:
undefined method `env' for nil:NilClass
RSpec is calling this:
def self.eval_before_eachs(example)
world.run_hook_filtered(:before, :each, self, example.example_group_instance, example)
ancestors.reverse.each { |ancestor| ancestor.run_hook(:before, :each, example.example_group_instance) }
end
# spec/support/http_referer.rb
module HttpReferer
def self.included(base)
base.class_eval do
setup :setup_http_referer if respond_to?(:setup)
end
end
def setup_http_referer
#request.env["HTTP_REFERER"] = "/back"
end
end
# spec/spec_helper.rb
RSpec.configure do |config|
config.include HttpReferer, :type => :controller
end
This link may help get you pointed in the right direction...
https://github.com/plataformatec/devise/wiki/How-To:-Controllers-and-Views-tests-with-Rails-3-%28and-rspec%29
You'll need to make a module like...
module Foo
def set_referer
#request.env["HTTP_REFERER"] = root_url
end
end
Then configure RSpec...
RSpec.configure do |config|
config.extend Foo, :type => :controller
end
Then call it in each of your controller specs...
describe MyController do
set_referer
end
We're using a similar approach to set our session cookie, but YMMV.