How can I test integration with RSpec and Devise - ruby-on-rails

I have quite some issues with testing the features of my rails application using RSpec. I have a company controller, and are using devise to make sure that you need to be logged in to access the controller.
I then have integration tests like the following:
describe "with valid information" do
before do
fill_in "company_address", with: "My special address"
fill_in "company_name", with: "My User"
fill_in "company_contact_email", with: "test#test.com"
fill_in "company_contact_name", with: "Someone Hello"
end
it "should create a company" do
expect { click_button submit }.to change(Company, :count).by(1)
end
end
It, of course, fails because I don't have a user who is logged in. I have tried creating a test helper like described here, and it works well for controllers, but not for integration tests.
What is the best way to log a user in with Devise, prior to these tests?

add file devise.rb to rspec/support
And here is content
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
Another way to solve your problem: Rails integration test with the devise gem

Related

Testing Devise Using capybara rspec and factory girl

i'm new at rails and i'm testing my devise gem User with capybara rspec and factory girl.
Here's spec code:
require 'rails_helper'
RSpec.describe User, type: :request do
it "displays the user's email after successful login" do
user = FactoryGirl.create(:user, email: 'test#test.com', password: 'password')
visit root_url
fill_in 'Email', with: 'test#test.com'
fill_in 'Password', with: 'password'
click_button 'Log in'
expect page.has_content?('jdoe')
end
end
The problem is in
expect page.has_content?('jdoe')
No matter what i put instead 'jdoe' test works perfectly without any errors.
Here's factory:
FactoryGirl.define do
factory :user do
email 'test#test.com'
password 'password'
password_confirmation 'password'
end
end
Maybe I missed something or going with a wrong way. How should I test here?
# spec/features/sessions_spec.rb
require 'rails_helper'
RSpec.feature "Sessions" do
scenario "displays the user's email after successful login" do
user = FactoryGirl.create(:user)
visit root_url
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
click_button 'Log in'
expect(page).to have_content("Signed in successfully")
# will give a false postitive if form is rerended?
expect(page).to have_content(user.email)
end
end
A few things here:
Use a feature and not a request spec for end to end testing.
Don't hardcode object attributes. The whole point of factories is that the factory takes care of generating unique data so that the tests don't give false positives due to residual state.
Use expect(page).to instead of expect page.should. expect(page).to sets a subject and uses a RSpec matcher which will show you the text of the page in an error message if it does not match.

Devise, looking for a good way to integration test the interaction between two users in a turn based system

Stack: Rails '4.0.4', devise, rSpec, factory_girl, cappybara + selenium-webdriver, mySQL
I'm stll finding myself a little confused controlling user auth in my tests, but this patchwork from other examples is working for now. I have a file called request_helpers.rb in /support that contains:
require 'spec_helper'
include Warden::Test::Helpers
module RequestHelpers
class Login
def self.create_logged_in_user
user = FactoryGirl.create(:user)
login(user)
user
end
def self.login(user)
login_as user, scope: :user, run_callbacks: false
end
end
end
And this is an example of a passing test:
require "spec_helper"
feature "Story Management" do
let( :authorized_user ){ RequestHelpers::Login.create_logged_in_user }
scenario "has a valid factory" do
authorized_user.should be_an_instance_of( User )
end
scenario "Can visit root", js:true do
visit root_path( authorized_user )
page.should have_content( "Your Stories" )
end
end
My question is, How can I logout my authorized user, and log in a new authorized user? Every attempt to utilize devise logout method in my request helper hasn't worked.
Here is my attempt at testing this:
require 'spec_helper'
include Warden::Test::Helpers
Warden.test_mode!
module RequestHelpers
class Login
def self.create_logged_in_user
user = FactoryGirl.create(:user)
login(user)
user
end
def self.login(user)
login_as user, scope: :user, run_callbacks: false
end
def self.logout(user)
logout( user )
end
end
end
scenario "Two users can take turns adding 3 chapters each" do
chapter_string = ValidString.short
player1 = create(:user)
player2 = create(:user)
RequestHelpers::Login.login(player1)
visit new_story_path( player1 )
fill_in "story_title", with: ValidString.short
fill_in "co_author", with: player2.email
click_button "Create Story"
click_link "New Chapter"
fill_in "chapter_body", with: chapter_string
click_button "Create Chapter"
page.should have_content(chapter_string)
RequestHelpers::Login.logout(player1)
RequestHelpers::Login.login(player2)
fill_in "chapter_body", with: chapter_string
click_button "Create Chapter"
page.should have_content(chapter_string)
end
Failed test text:
1) Chapter Management Two users can take turns adding 3 chapters each
Failure/Error: Unable to find matching line from backtrace
SystemStackError:
stack level too deep
# ./spec/support/request_helpers.rb:16
I decided to stop trying to hack around devise here, and just fill in the forms like they suggested in the docs. Logging in and out works fine in this scenario, although slower.
From Devise docs:
These helpers are not going to work for integration tests driven by Capybara or Webrat. They are meant to be used with functional tests only. Instead, fill in the form or explicitly set the user in session.

Trouble with refactoring code for rspec feature tests in rails 4

I am trying to give the user of my web app the ability to login with a password. I am rolling my own authentication instead of using a gem. I read this article about refactoring Rspec/Capybara tests:
http://robots.thoughtbot.com/rspec-integration-tests-with-capybara
I liked what I read and decided to give refactoring a try. I created a session helper file for my feature tests.
module Features
module SessionHelpers
def sign_in
user = create(:user)
visit '/authentications/new'
fill_in 'Login', with: user.name
fill_in 'Password', with: user.password
click_button 'Sign in'
end
end
end
I then called the sign_in function in my login tests. Here is a little sample.
require 'spec_helper'
feature "signing in" do
before :each do
User.create(:name => 'user#example.com', :password => 'caplin')
end
scenario "user who logs in with correct credentials" do
sign_in
expect(page).to have_content 'Hi user#example.com'
end
end
Unfortunately, I keep getting this error message:
2) signing in user who logs in with correct credentials
Failure/Error: sign_in
NoMethodError:
undefined method `create' for #<RSpec::Core::ExampleGroup::Nested_3:0x007ffc85012438>
# ./spec/support/features/session_helpers.rb:4:in `sign_in'
# ./spec/features/user_logs_in_spec.rb:13:in `block (2 levels) in <top (required)>'
Basically, I need some way to grab the user I created and pass it into the sign_in function. Any hints?
I'm guessing your first issue is a different test configuration than the one the ThoughBot example has. create is not to my knowledge a default method available in RSpec; I'm going to guess they've added every FactoryGirl method to the testing scope. If you're using FactoryGirl, you can get the same behavior by just namespacing the create command:
def sign_in
user = FactoryGirl.create(:user)
visit '/authentications/new'
fill_in 'Login', with: user.name
fill_in 'Password', with: user.password
click_button 'Sign in'
end
However, this won't quite get you everything that you asked for, since you still won't be able to add a custom user. An easy way for this would allow for a user to be passed in:
def sign_in(user=nil)
user ||= FactoryGirl.create(:user)
...
end
This will create the user for you if you don't pass one in on the sign_in call.
Going back to the spec you posted, you'd want to change it to this:
feature "signing in" do
before :each do
#user = User.create(:name => 'user#example.com', :password => 'caplin')
end
scenario "user who logs in with correct credentials" do
sign_in(#user)
expect(page).to have_content 'Hi user#example.com'
end
end
You'd need to attach the user you created to a variable (#user), then pass it to the sign_in as needed.
Problem in you model
module Features
module SessionHelpers
def sign_in
user = create(:user) # <- this method allow only in FactoryGirl
visit '/authentications/new'
fill_in 'Login', with: user.name
fill_in 'Password', with: user.password
click_button 'Sign in'
end
end
end
i use another way. Create a class and include FactroyGirl methods and Capybara::DSL like this
class Features
include FactoryGirl::Syntax::Methods
include Capybara::DSL
def sign_in
user = create(:user) #<- FactroyGirl
visit '/authentications/new' #<- Capybara
fill_in 'Login', with: user.name #<- Capybara
fill_in 'Password', with: user.password #<- Capybara
click_button 'Sign in' #<- Capybara
self #<- return page
end
end
in spec
feature "signing in" do
let(:login_user) { Features.new }
scenario "user who logs in with correct credentials" do
page = login_user.sign_in
expect(page).to have_content 'Hi user#example.com'
end
end
You can accomplish this by including FactoryGirl in your tests. Your RSpec configuration block (in spec_helper.rb or in the new version of RSpec rails_helper.rb) should look like this:
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
end

Using Cucumber, is there a way to log a user in without the interface?

The vast majority of my cucumber features require the user to be logged in. However I don't really need to test the login functionality for every single test. I'm currently using Devise for my authentication.
I'm looking for a way to sign a user in with devise, without filling out the sign in form. Is there anyway to do this? I would prefer to not have to use the sign in action for every test.
No, there is no way. In the documentation, with regard to the sign_in #user and sign_out #user helper methods, it says:
These helpers are not going to work for integration tests driven by Capybara or Webrat. They are meant to be used with functional tests only. Instead, fill in the form or explicitly set the user in session
As you said yourself, it is probably cleanest to do it with a before :each block. I like to structure it like the following:
context "login necessary" do
# Before block
before do
visit new_user_session_path
fill_in "Email", with: "test#test.com"
fill_in "Password", with: "password"
click_button "Login"
assert_contain "You logged in successfully."
end
# Actual tests that require the user to be logged in
it "does everything correctly" do
# ...
end
end
context "login not necessary" do
it "does stuff" do
# code
end
end
I found this to be quite useful, since if I change authentication rules (i.e. whether or not the user has to be logged in for a specific path) I can just take the whole test and move it into the other description block, without changing any more code.
Generally, you should always test through the interface. But I think this is an acceptable exception.
I'm using devise with capybara with rspec but it should work for you too.
In a helper I have this:
module LoginHelper
def login_as(user)
super(user, :scope => :user, :run_callbacks => false)
end
end
RSpec.configure do |config|
config.include Warden::Test::Helpers, :type => :feature
config.include LoginHelper, :type => :feature
config.before :each, :type => :feature do
Warden.test_mode!
end
config.after :each, :type => :feature do
Warden.test_reset!
end
end
Then in the feature:
background do
login_as(user)
visit root_path
end
Also see:
How to Stub out Warden/Devise with Rspec in Capybara test

config.cache_classes = false messing up rspec tests?

I'm following the Ruby on Rails Tutorial by Michael Hartl (railstutorial.org).
At some point I got tired of tests failing just bescause the tests used old cached versions of classes, so I turned off config.cache_classes in the test environment. That fixed the issue and everything went good for some time.
Until I tried implementing the Integration tests in Chapter 8.4.3. At this point the data entered into the database with
it "should make a new user" do
lambda do
visit signup_path
fill_in "Name", :with => "Example User"
fill_in "Email", :with => "user#example.com"
fill_in "Password", :with => "foobar"
fill_in "Confirmation", :with => "foobar"
click_button
response.should have_selector("div.flash.success",
:content => "Welcome")
response.should render_template('users/show')
end.should change(User, :count).by(1)
end
would remain in the Database after each test, so only the first time this test ran it would work, after that it always fails until i manually empty the database.
Apart from that it worked.
But now in chapter 9, again the integration test fails:
describe "when signed in" do
before(:each) do
#user = Factory(:user)
visit signin_path
fill_in :email, :with => #user.email
fill_in :password, :with => #user.password
click_button
end
it "should have a signout link" do
visit root_path
response.should have_selector("a", :href => signout_path,
:content => "Sign out")
end
This time it just doesn't work, the user is not getting logged in and the resulting page has no sign out link, just the normal sign in link.
When testing this in a webbrowser it works fine.
It took me hours and days of searching the internet and testing different stuff and finally I found the solution: Turning config.cache_classes back on.
Now it works flawlessly.
So can anyone explain to me why config.cache_classes makes the tests fail? And how can I turn off caching without messing up my tests?
Thanks in Advance,
Best regards, Tobias
When you make a Capybara call, it uses rack-test to emulate a call to the rails app. Every time a call is completed, it reloads all rails classes. What this means is that the #user object you created before you called 'visit signin_path' gets nil'd out because all ActiveRecord objects have been reloaded.
When you set cache-classes to true, it tells Rack not to reload the ActiveRecord objects on every request, so your tests pass again.
I believe that if you want the test you wrote above to pass without turning on cache-classes, you should move the '#user = Factory(:user)' line below the 'visit signin_path' line.
I had exactly the same problem and like you setting config.cache_classes to true solved the problem. But caching classes in the test environment really isn't what you want I don't think. I certainly don't understand why caching classes makes the tests pass.
So the way I found to solve this was to install a database cleane as the reason the tests are failing is due to duplicate entries in the test database. https://github.com/bmabey/database_cleaner
Then in your gemfile, in your test group add this.
gem 'database_cleaner', '0.6.6'
then run "bundle install" to install this gem
Then... in your spec_helper.rb file, add this..
RSpec.configure do |config|
.
.
.
.
config.before(:each) do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean
end
This will clear your test database before each run of your rspec tests.
Hope this helps.
Cheers,
mark.

Resources