I ran cucumber for the devise-rspec-cucumber project but the following scenario is failing:
Scenario: User is not signed up
Given I do not exist as a user
When I sign in with valid credentials
Then I see an invalid login message
And I should be signed out
undefined method `flatten' for nil:NilClass (NoMethodError)
1 scenario (1 failed)
4 steps (4 passed)
I tried removing every step except the first one and it is still failing:
Given /^I do not exist as a user$/ do
create_visitor
delete_user
end
where
def create_visitor
#visitor ||= { :name => "Testy McUserton", :email => "example#example.com",
:password => "changeme", :password_confirmation => "changeme" }
end
def delete_user
#user ||= User.where(:email => #visitor[:email]).first
#user.destroy unless #user.nil?
end
But if I replace create_visitor with create_user, it will pass.
def create_user
create_visitor
delete_user
#user = FactoryGirl.create(:user, #visitor)
end
I am confused what's going on. It seems to me that the step definition is expecting some things that I am not providing.
I got the same error message before. I was sure that there's nothing wrong with my steps. Therefore I tried to upgrade the database_cleaner gem to the latest (1.0.1) since the error was caused by database_cleaner. And it did solve the problem. I would suggest you try this if your database_cleaner gem version is not 1.0.1. Just do
$ bundle update database_cleaner
The problem exist here:
create_visitor
delete_user
In the first step you created a visitor, but not an user. Then in second step you try to delete an user who does not exist. That's why the test failed.
I suggest you to review the logic and create correct steps.
Related
EDIT Read my comment to this question
I'm very new to rails, so please bear with me.
I've been trying to configure a test for Devise using factory girl and rspec. This has taken me the best part of 2 hours, and scouring half the internet to no avail. Even though there is loads of thread on what seems to be my issue, I just cant figure it out.
This is how my /spec files looks like.
GET Home Gives the correct status code
Failure/Error: sign_in user
NoMethodError:
undefined method `sign_in' for #<RSpec::Core::ExampleGroup::Nested_2:0x00000106f32558>
# ./spec/models/user_spec.rb:6:in `block (2 levels) in <top (required)>
This is the error message I get, trying to achieve the following test:
user_spec.rb:
require 'spec_helper'
describe "GET Home" do
before do
##I have tried all sorts of things here. I have also tried to define a module in devise.rb (see note below*), and then call that module here instead of the 2 lines below. But I get the same error, no local variable or undefined method for ...
user = FactoryGirl.create(:user)
sign_in user
end
describe "GET /Home"
it "Gives the correct status code" do
get root_path
response.status.should be(200)
end
end
in spec/factories/users.rb:
FactoryGirl.define do
factory :user do
name "Christoffer"
email "test#test2.com"
password "testtest"
password_confirmation "testtest"
end
end
And the folling lines is included in spec_helpers.rb
config.include FactoryGirl::Syntax::Methods
config.include Devise::TestHelpers, :type => :controller
Now, by doing this, i get the error above. Can anyone possibly explain what I'm doing wrong here? It might be something really obvious, as I'm not really that well rehearsed in the ways of Rails.
*Note (module I tried to define in devise.rb and insert in the before do):
module ValidUserRequestHelper
# Define a method which signs in as a valid user.
def sign_in_as_a_valid_user_nommels
# ASk factory girl to generate a valid user for us.
#user ||= FactoryGirl.create :user
# We action the login request using the parameters before we begin.
# The login requests will match these to the user we just created in the factory, and authenticate us.
post_via_redirect user_session_path, 'user[email]' => #user.email, 'user[password]' => #user.password
end
end
The purpose of 'spec/requests' is for integration tests. You would test features of your app from the user's perspective (ie. fill in certain info, then click button, then so and so should happen if certain inputs are valid or invalid). Spec/models and spec/controllers are usually for unit tests where you test for smaller parts of your app (ie. what happens if the password and password_confirmation params passed to your user model don't match)
class Spinach::Features::Signup < Spinach::FeatureSteps
attr_accessor :valid_attributes
before do
valid_attributes = Fabricate.attributes_for(:identity)
##valid_attributes = Fabricate :identity
end
step 'I am a visitor' do
true
visit root_path
end
step 'I am on the landing page' do
current_path.must_equal root_path
end
step 'I follow signup link' do
click_link('signup_link')
end
step 'I fill name with my name' do
fill_in 'name', with: valid_attributes.name
end
step 'I fill email with my email' do
fill_in "email", with: valid_attributes.email
end
end
i use spinach gem for creating feature steps. above code id my feature steps. i also use minitest for testing framework. i use fabricator gem for creating random datas.
require "ffaker"
Fabricator(:identity) do
name {Faker::Name.name}
email {Faker::Internet.email}
password_digest "ChtUIGTiBvrm6v6R4PX6sO3netSuN3eW0AbFmXblXvgKM5Z8sFUKy"
end
this is my fabricator class for identity model. when i run signup feature, i see an error:
undefined method `name' for nil:NilClass
i think that it is about Fabricate.Attributes_for. if i use Fabricate :identity, it doesn't give error.
i couldn't solve this. Any ideas? Thanks in advance.
When you do:
valid_attributes = Fabricate.attributes_for(:identity)
You've got a Hash.
So do: valid_attributes[:email] or use an Openstruct.
I'm working through Michael Hartl's Rails Tutorial. I've come to Chapter 9, Exercise 1. It asks you to add a test to verify that the admin attribute of the User class is not accessible. Here's the User class with irrelevant portions commented out:
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
attr_protected :admin
# before_save methods
# validations
# private methods
end
And here's the test I'm using to validate that the admin attribute is not accessible.
describe User do
before do
#user = User.new(
name: "Example User",
email: "user#example.com",
password: "foobar123",
password_confirmation: "foobar123")
end
subject { #user }
describe "accessible attributes" do
it "should not allow access to admin" do
expect do
#user.admin = true
end.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
end
end
end
The test fails. It says no errors were raised, in spite of the fact that the admin attribute is protected. How can I get the test to pass?
From the Ruby documentation:
Mass assignment security provides an interface for protecting attributes from end-user assignment.
http://api.rubyonrails.org/classes/ActiveModel/MassAssignmentSecurity/ClassMethods.html
Try this code instead
describe "accesible attributes" do
it "should not allow access to admin" do
expect do
User.new(admin: true)
end.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
end
end
As Rails docs claim about attr_protected
Attributes named in this macro are protected from mass-assignment, such as new(attributes), update_attributes(attributes), or attributes=(attributes).
So you can change field manually. 'attr_protected' is only about mass-assignment.
This only works for mass assignments like setting the field from a form submit. Try something like this:
#user.update_attrtibutes(:admin => true)
#user.admin.should be_false
#agaved. This answer may come late and you may already have the answer but I wanted to answer your question, it may help somebody else.
The best way to understand how update_attributes differs from direct assignment
#user.admin = true is to try and do it in your console. If you are following Hartl's tutorial, try the following:
#user = User.first
#user.admin?
=> true
#user.admin = false
=> false
Direct assignment manages to change the value of the attribute admin for user from true to false without raising a Mass Assignment Error. This is because Mass Assignment Errors are raised when you call update_attributes or create a new user User.new using an attribute that is not accessible. In other words, Rails raises mass assignment errors when a user tries to update (attribute_update) or create User.new(admin: true) a new user with attributes that are not accessible to her. In the above case, direct assignment is not using the create or update methods of the user controller.
They are very similar pieces of code since you can use direct assignment to force a change in the admin attribute in the above case using #user.save!(validate: false) directly in IRB but as I said above this does not use the create or update method of your user controller and, hence, it will not throw the error.
I hope that helps, this helped me.
[Spoiler alert: If you are trying to solve the exercises in Hartl's book on your own, I'm pretty sure I'm about to give the answer away. Even though the answer that has been accepted is interesting information, I don't believe it was what Hartl had in mind as that would require knowledge the book has not covered and also does not relate it specifically to updates via web action or use the test he provides.]
I think you might be thinking this exercise is a lot harder than it actually is, if I got it right. First of all, you have misunderstood the hint:
Hint: Your first step should be to add admin to the list of permitted parameters in user_params.
It does not say to change its attr declaration in the class. It says to modify the helper function user_params. So I added it to the list in users_controller.rb:
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation, :admin)
end
Next, I copied the code in Listing 9.48 to the indicated place in spec/requests/user_pages_spec.rb:
require 'spec_helper'
describe "User pages" do
.
.
.
describe "edit" do
.
.
.
describe "forbidden attributes" do
let(:params) do
{ user: { admin: true, password: user.password,
password_confirmation: user.password } }
end
before do
sign_in user, no_capybara: true
patch user_path(user), params
end
specify { expect(user.reload).not_to be_admin }
end
end
end
The test then fails, showing that it is possible to pass in an admin parameter and thus change a normal user to an admin, which is not what you would want to allow:
$ rspec spec
.....................[edited out dots].................................F
Failures:
1) User pages edit forbidden attributes
Failure/Error: specify { expect(user.reload).not_to be_admin }
expected admin? to return false, got true
# ./spec/requests/user_pages_spec.rb:180:in `block (4 levels) in <top (required)>'
Finished in 4.15 seconds
91 examples, 1 failure
Failed examples:
rspec ./spec/requests/user_pages_spec.rb:180 # User pages edit forbidden attributes
Then, to make it impossible to pass in an admin value via a web action, I simply removed :admin from the list of acceptable user_params, undoing the first change:
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
Now the attempt to patch the user with a new admin value fails... and the test for it succeeds, verifying "that the admin attribute isn’t editable through the web."
$ rspec spec
...........................................................................................
Finished in 4.2 seconds
91 examples, 0 failures
Following the hint, I first added :admin to attr_accessible in app/models/user.rb to start with a red.
I then added the test:
describe "admin attribute" do
it "should not be accessible" do
expect do
#user.update_attributes(:admin => true)
end.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
end
end
to the spec and got a red.
Removing :admin from user.rb I get a green. So far so good.
What puzzles me is why I should use the sintax:
#user.update_attributes(:admin => true)
instead of #user.admin = true (I checked and in this case it doesn't work).
Working with RSpec & Capybara, I'm getting an interesting test failure mode which goes away with a few subtle rearrangements of lines in the test case...stuff that shouldn't matter.
I'm developing my own authentication system. It is currently working and I can login/out with the browser and the session works etc etc. However, trying to test this is failing. Something is going on that I don't quite understand, which seems to depend on the order of (seemingly) unrelated calls.
require 'spec_helper'
describe "Sessions" do
it 'allows user to login' do
#line one
user = Factory(:user)
#For SO, this method hashes the input password and saves the record
user.password! '2468'
#line two
visit '/sessions/index'
fill_in 'Email', :with => user.email
fill_in 'Password', :with => '2468'
click_button 'Sign in'
page.should have_content('Logged in')
end
end
As is, that test fails...the login fails. After inserting 'debugger' calls into both the spec and the controller I can see why: the user is not getting inserted into the database as far as the controller is concerned:
Edit adding in ApplicationController
class ApplicationController < ActionController::Base
helper :all
protect_from_forgery
helper_method :user_signed_in?, :guest_user?, :current_user
def user_signed_in?
!(session[:user_id].nil? || current_user.new_record?)
end
def guest_user?
current_user.new_record?
end
def current_user
#current_user ||= session[:user_id].nil? ? User.new : User.find(session[:user_id])
rescue ActiveRecord::RecordNotFound
#current_user = User.new
flash[:notice] = 'You\'ve been logged out.'
end
end
class SessionsController < ApplicationController
def login
user = User.where(:email=>params[:user][:email]).first
debugger ###
if !user.nil? && user.valid_password?(params[:user][:password])
#engage session
else
#run away
end
end
def logout
reset_session
redirect_to root_path, :notice => 'Logget Out.'
end
end
in the console, at the above breakpoint:
1.9.2 vox#Alpha:~/Sites/website$ rspec spec/controllers/sessions_controller_spec.rb
/Users/vox/Sites/website/app/controllers/sessions_controller.rb:7
if !user.nil? && user.valid_password?(params[:user][:password])
(rdb:1) irb
ruby-1.9.2-p180 :001 > User.all.count
=> 0
ruby-1.9.2-p180 :002 >
However, if I rearrange a few lines in my test, putting line 'two' above line 'one':
describe "Sessions" do
it 'allows user to login' do
#line two
visit '/sessions/index'
#line one
user = Factory(:user)
#For SO, this method hashes the input password and saves the record
user.password! '2468'
fill_in 'Email', :with => user.email
fill_in 'Password', :with => '2468'
click_button 'Sign in'
page.should have_content('Logged in')
end
end
I get this in the console (same breakpoint as above):
1.9.2 vox#Alpha:~/Sites/website$ rspec spec/controllers/sessions_controller_spec.rb
/Users/vox/Sites/website/app/controllers/sessions_controller.rb:7
if !user.nil? && user.valid_password?(params[:user][:password])
(rdb:1) irb
ruby-1.9.2-p180 :001 > User.all.count
=> 1
For the sake of brevity I've omitted the full dump of the contents of the user object but I can assure you that the test completes as expected.
This behavior of swapping lines to get the test to pass doesn't really fit well with my idea of what should be going on with these commands and has proven to be quite a bear to my testing in other areas.
Any hints as to what is going on here?
I've scoured google and SO for ideas which present this problem, and there are no shortage of SO questions about RSpec/Capybara and Sessions. Nothing seemed to fit quite right though.
Thanks for looking.
Update
I've added a breakpoint (just before a visit call) and some debugging to the test and come back with this:
(rdb:1) user
#<User id: 1, login_count: 1, email: "testuser1#website.com", encrypted_password: "11f40764d011926eccd5a102c532a2b469d8e71249f3c6e2f8b...", salt: "1313613794">
(rdb:1) User.all
[#<User id: 1, login_count: 1, email: "testuser1#website.com", encrypted_password: "11f40764d011926eccd5a102c532a2b469d8e71249f3c6e2f8b...", salt: "1313613794">]
(rdb:1) next
/Users/vox/Sites/website/spec/controllers/sessions_controller_spec.rb:19
fill_in 'Email', :with => user.email
(rdb:1) User.all
[]
So clearly something along the way that visit does is telling Factory Girl that its done with the user object and so she deletes it?
Edit After inspecting test.log carefully, nothing is issuing any delete. So I'm more or less back to square one.
With the help of the Factory Girl mailing list I've found the issue.
By default RSpec uses transactions to maintain the database in a clean state and each transaction is tied to a thread. Somewhere along the pipeline the visit_page command splits off and the transaction tied to the current thread dies.
The solution is simple: disable transactions.
describe "Sessions" do
self.use_transactional_fixtures = false
it 'no longer uses transactions' do
#whatever you want
end
end
Update for Rails 5.1
As of Rails 5.1, use_transactional_fixtures is deprecated and should be replaced with use_transactional_tests.
self.use_transactional_tests = false
I think the user variable in RSpec has overwritten the one in the controller so it didn't work ? (couldn't get right user.email in the test)
Before :
user = Factory(:user)
user.password! '2468'
visit '/sessions/index' # user gets overwritten
fill_in 'Email', :with => user.email # can't get user.email
After :
visit '/sessions/index' # Execute action
user = Factory(:user) # user gets overwritten
user.password! '2468'
fill_in 'Email', :with => user.email # user.email works
This isn't technically an answer, more of a comment but to clarify the code it's the easiest mechanism.
Can you try doing the following to help narrow down where the user's being destroyed
describe "Sessions" do
it 'allows user to login' do
#line one
user = Factory(:user)
#For SO, this method hashes the input password and saves the record
user.password! '2468'
# check the user's definitely there before page load
puts User.first
#line two
visit '/sessions/index'
# check the user's still there after page load
puts User.first.reload
fill_in 'Email', :with => user.email
fill_in 'Password', :with => '2468'
click_button 'Sign in'
# check the user's still there on submission (though evidently not)
puts User.first.reload
page.should have_content('Logged in')
end
end
EDIT
The fact that it works for you ok in real life but not in Capybara suggests that it may be a product of existing session information. When you're testing in the browser you're usually going off the back of previous work but Capybara is always starting from a clean session.
You can easily see if you can reproduce the Capybara error in-browser by clearing all your cookies (as I'm sure you know) or by just switching to a new incognito window in Chrome/FF which is a nice quick way to get a clean session.
The correct answer above helped me. Of course, I needed to change some other tests that (wrongly or rightly) assumed a fixture did not exist. For more information: there's some info about this in the Capybara README.
https://github.com/jnicklas/capybara
"If you are using a SQL database, it is common to run every test in a transaction, which is rolled back at the end of the test, rspec-rails does this by default out of the box for example. Since transactions are usually not shared across threads, this will cause data you have put into the database in your test code to be invisible to Capybara."
You can also config RSpec to clean up after your test manually:
https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Cleanup-after-your-Rspec-tests
I'm trying to run a Capybara 1.0 test on my Rails 3 app to test whether when a user clicks on a confirmation link, he is actually confirmed.
Now, this actually works when I test it manually. In addition, as you can see there is a puts #user.confirmed line that I put in the confirm method to debug this, and it actually prints true when I run the test. However, the test itself fails.
It seems as if the confirmed attribute in my user model isn't being remembered by the test after executing the controller method.
What am I missing? Thanks so much in advance.
Test:
it "should allow a user to be confirmed after clicking confirmation link" do
fill_in('user_email', :with => 'test#test.com')
click_button('Submit')
#user = User.find_by_email('test#test.com')
#user.confirmed.should be_false
visit confirm_path(#user.confirmation_code)
#user.confirmed.should be_true
end
Controller method:
def confirm
#confirmation_code = params[:confirmation_code]
#user = User.find_by_confirmation_code(#confirmation_code)
#website = #user.website
#user.confirm
if #user.referrer_id
User.find(#user.referrer_id).increment_signups
end
flash[:success] = "Thanks for signing up!"
flash[:user_show] = #user.id
puts #user.confirmed
redirect_to "http://" + #website.domain_name
end
User model method:
def confirm
self.confirmed = true
self.save
end
Is it that you would need to reload the user object after visiting the confirm_path? Try this:
it "should allow a user to be confirmed after clicking confirmation link" do
fill_in('user_email', :with => 'test#test.com')
click_button('Submit')
#user = User.find_by_email('test#test.com')
#user.confirmed.should be_false
visit confirm_path(#user.confirmation_code)
#user = User.find_by_email('test#test.com')
#user.confirmed.should be_true
end
Alternatively you could use #user.reload.
The user object referred to in your test is just a copy of the object being manipulated by the application so it is not going to be automatically refreshed. You need to fetch it from the database a second time to get the updated values.