Im on chp 9 of Michael Hartls Ruby On Rails Tutorial. In my authentication_pages_specs.rb the 'submitting to the update action' is causing the spec to crash with the error "undefined local variable or method `user' for # (NameError)"
require 'spec_helper'
describe "Authentication" do
subject { page }
describe "authorization" do
describe "for non signed-in users" do
let(:user) { FactoryGirl.create(:user) }
describe "in the users controller" do
describe "visiting the edit page" do
before { visit edit_user_path(user)}
it { should have_selector('title', text: 'Sign in')}
end
describe "submitting to the update action" do
describe { put user_path(user)} #Error on this line
specify { response.should redirect_to(signin_path) }
end
end
end
end
end
Whats cauing the spec to crash ?
Thank You
Putting in before block the put part fixes the problem
Related
I have been struggling to using ruby/rspec/capybara/devise to test my code. A simple test I am trying to write is for signing in. I have a valid user sign in and expect to see an h1 tag as defined in the following code:
describe "Authentication" do
subject { page }
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
before { sign_in_with user.email }
it { should have_css('h1', text: "Welcome to the Test") }
end
end
Problem is that I get this in return:
1) Authentication signin page with valid information
Failure/Error: it { should have_css('h1', text: "Welcome to the Test") }
expected css "h1" with text "Welcome to the Test" to return something
# ./spec/requests/authentication_pages_spec.rb:34:in `block (4 levels) in <top (required)>'
Is there a way to output what the test found in the h1 (or that it didn't find it at all?) instead of it didn't find what it expected? There is a good chance my sign_in method is not working, but I can't validate that because I'm not sure what the test sees after sign_in_with executes.
Thanks and happy to provide more context if it's helpful.
EDIT
Updated code to reflect subject of tests.
... I'm not sure what the test sees after sign_in_with executes.
You can open a snapshot of the current page with save_and_open_page:
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
before { sign_in_with user.email }
it { save_and_open_page; should have_css('h1', text: "Welcome to the Test") }
end
There is no subject, you can't use it to represent result. For Capybrara, you need to check the page object returned.
Let's rewrite the test like this:
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
before do
sign_in_with user.email
end
it "signs in successfully" do
expect(page).to have_css('h1', text: "Welcome to the Test")
end
end
Or better, with Capybara story DSL
feature "User sign in" do
given(:user) { FactoryGirl.create(:user) }
scenario "with valid information " do
sign_in_with user.email
expect(page).to have_css('h1', text: "Welcome to the Test")
end
scenario "with invalid filing" do
sign_in_with "foo#bar.com"
expect(page).to have_text("Invalid email or password")
end
end
I have the following code in my requests spec:
describe 'Poll' do
subject { page }
context 'as system admin' do
let(:user) { Fabricate(:system_admin) }
before { login user}
it 'is accessible' do
visit '/admin/poll'
current_path.should == '/admin/poll'
end
describe 'sending poll' do
it 'sends to all users' do
save_and_open_page
end
end
end
end
The login user doesn't seem to work even if the method seems to be working fine. I tried using login user inside the it 'is accessible' do block and that specs works fine if I do it that way. If I remove it from there and put it in a before block like above. The user doesn't stay signed in. I put in a save_and_open_page to debug and I get this notification in the page:
Your account was not activated yet. If a reset password link was sent to you, use that link to change your password.
I'm using Devise, RSpec, Capybara and Rails 3. I've also set user to confirm! in my Fabrication file. Below is how it looks:
Fabricator(:system_admin) do
first_name { sequence(:first_name) { |n| "Person#{n}"} }
last_name { sequence(:last_name) {|n| "#{n}" } }
email { sequence(:email) { |n| "person_#{n}#example.com"} }
password "foobar"
password_confirmation "foobar"
company_name { sequence(:company_name) { |n| "google#{n}" } }
role "system_admin"
after_create do |user|
user.confirm!
user.create_company
end
end
Question: What could be the problem? How come the user isn't staying logged in and why do I get that message saying that I should activate my account? Isn't user.confirm! enough?
could this be the problem?
Fabricate(:system_admin) != Fabricator(:system_admin)
So if you debug your save_and_open_page and it tells you that the account is not activated it seemes your fabricate is not working properly. have you tried and debug that?
what does your save_and_open_page do? does it try to use the user for something? because I have experienced when defined with a let, if not touched the variable(user in this case) then it does no exist on that context. besides. whats the error when you run the specs like this on it "is acessible"? just says there is no user logged in?
so you can either stub your methods for login(for example if you have method called current_user that gives you the logged in user or something) or instead of using let, instiate like:
user = Fabricate(:system_admin)
but hey there is a lot of good advices here:
http://betterspecs.org/
it seems like your blocks context and describe are too complex. I am also not following this guidelines 100% but I think I should and you too would benefit from this.
let me know if you found out another reason why its not working!
I think before(:each) should resolve the problem
Add this Devise method:
confirmed_at { Time.now }
So your after_create method should looks like:
after_create do |user|
user.confirm!
user.confirmed_at { Time.now }
user.create_company
end
I've seen this issue several places, but none of the solutions seem to work.
I have a Rails 3.1 app with the latest versions of guard, spork, factory girl, rspec, and devise.
Whenever I try to create a user factory (the user model is a devise model) then I get this error:
Could not find a valid mapping for #<User...model attributes...>
I'm not sure what the problem is.
I ran rake db:test:prepare. I followed the instructions in this stackoverflow question: "Could not find a valid mapping for #<User ...>" only on second and successive tests
ALso, I attempted the solution in this answer from google groups:
https://groups.google.com/forum/?fromgroups#!topic/plataformatec-devise/StpbEsDCec0[1-25]
And, here's all the relevant code:
Guardfile
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
require 'capybara/rspec'
guard 'spork', :cucumber_env => { 'RAILS_ENV' => 'test' }, :rspec_env => { 'RAILS_ENV' => 'test' } do
watch('config/application.rb')
watch('config/environment.rb')
watch('config/environments/test.rb')
watch(%r{^config/initializers/.+\.rb$})
watch('Gemfile')
watch('Gemfile.lock')
watch('spec/spec_helper.rb') { :rspec }
watch('test/test_helper.rb') { :test_unit }
watch(%r{features/support/}) { :cucumber }
end
guard 'rspec', :version => 2, :cli => '--drb' do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
# Rails example
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
watch('config/routes.rb') { "spec/routing" }
watch('app/controllers/application_controller.rb') { "spec/controllers" }
# Capybara request specs
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
# Turnip features and steps
watch(%r{^spec/acceptance/(.+)\.feature$})
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
end
</code>
This is in my spec/factories.rb
FactoryGirl.define do
load "#{Rails.root}/app/models/user.rb"
factory :user, class: User do |user|
email 'owner#example.com'
password '12345678'
password_confirmation '12345678'
companyid 'example_company'
end
end
This is my spec/controllers/api_controller_spec.rb
require 'spec_helper'
describe ApiController do
it 'verifies company_id through POST to api/company_id' do
load "#{Rails.root}/app/models/user.rb"
debugger
user = FactoryGirl.create(:user)
post(:get_company_id, {:company_id => 'example_company'})
response.body.should include('true')
end
end
And I have this at the end of my config/application.rb
ActionDispatch::Callbacks.after do
# Reload the factories
return unless (Rails.env.development? || Rails.env.test?)
unless FactoryGirl.factories.blank? # first init will load factories, this should only run on subsequent reloads
FactoryGirl.factories.clear
FactoryGirl.find_definitions
end
end
I'm really desperate for an answer here because otherwise I won't be able to test my User model (which is the most important model I have).
Feel free to comment and ask any questions.
EDIT: code looked funny in places, so I edited it for clarity
UPDATE:
So I tried simplifying everything to get to the core of the problem, and I'm pretty sure that devise and factory girl don't "like" each other. I'm still getting the exact same error whenever I try and create a user factory.
This is my new setup (I reverted to a previous git commit and I no longer have guard or spork).
My factories.rb is exactly the same as Michael Durant's except I have an extra line:
companyid 'example'
That's just a requirement for my app.
My spec_helper.rb requires rubygems and capybara/rspec and that's it.
And this is my spec/models/user_spec.rb
require 'spec_helper'
describe 'User associations' do
it 'tests creation of user' do
debugger
user = FactoryGirl.create(:user)
User.count.should be(1)
end
end
Also, this is interesting: When I hit that debugger statement and type in
eval User
It shows the mapping of a valid User.
UPDATE:
So, it's not factory girl that's the problem. It's devise.
This is the new api_controller_spec.rb file and it comes up with the same error of not having a valid mapping of the user.
require 'spec_helper'
describe ApiController do
it 'verifies company_id through POST to api/company_id' do
load "#{Rails.root}/app/models/user.rb"
debugger
user = User.new
user.email = 'owner#example.com'
user.password = '12345678'
user.password_confirmation = '12345678'
user.company_id = 'company'
user.save
post(:get_company_id, {:company_id => 'example_company'})
response.body.should include('true')
end
end
THere isn't a problem with any other environment as I can create users fine through the console, while a local server is running, or when the code is pushed up to Heroku. It might be rspec or something else, but I'm just not sure at this point.
I would recommend you simplify things to find the issue. Currently I feel you have too much going on / too many variable factors.
I would recommend the following:
1 Make a new branch. I assume you are using git, if not use it (git init) and make a fork.
2 Remove all the spork and guard stuff. They are helpful in speeding up your tests and running tests in a CI (Continuous Integration), but they are certainly not 'needed' and removing them will help uncover what the real problems are.
3 Set up your user factory correctly. We use this:
FactoryGirl.define do
sequence :email do |n|
"email#{n}#factory.com"
end
factory :user do
email
first_name { 'First' }
last_name { 'Last' }
password { "password" }
password_confirmation { "password" }
association :area
role { 'super_user' }
end
end
4 Set up your spec_help correctly.
We use these requires in our spec_helper.rb:
require 'rubygems'
require 'capybara/rspec'
5 Try to get one user test to pass using spec/models/user_spec.rb, something like:
require 'spec_helper'
describe 'User associations' do
subject { User.new }
it { should validate_presence_of :area }
...
So, the answer had nothing to do with guard, spork, rspec, or factory_girl.
The problem was that I had my devise_for :users routes commented out since I've been doing a huge overhaul of my rails app.
It's always something stupidly simple >.<
I'm using Capybara 1.1.2, Rails 3.1.3, rspec-rails 2.9.0, and Ruby 1.9.3p0.
Assume an app with standard and account_admin users. A standard user can create another standard user, but a standard user cannot create an account_admin user.
Of course the UI does not give the standard user the option of creating an account admin. But 30 seconds with Firebug and the user can re-write the HTML so it submits a POST request to create an account_admin.
How do I test that my app prevents this kind of simple hack?
The normal standard user test looks like this:
context "when standard user is signed in" do
before do
login_as standard_user
visit users_path # go to index
click_link('Add user') # click link like user would
end
describe "when fields are filled in" do
let(:new_email) { "new_user#example.com" }
before do
fill_in "Email", with: new_email
fill_in "Password", with: "password"
fill_in "Password confirmation", with: "password"
choose "Standard user" # radio button for Role
end
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
end
end
Is there a way to "fool" the test into taking a value not allowed on the form? I tried treating the radio button like a text field, but Capybara rejects it as a non-existent field:
fill_in "Role", with: "account_admin" # doesn't work
Direct modification of the params hash doesn't work either:
params[:role] = "account_admin" # doesn't work
Do I have to write this more like a controller test, with a direct call to post :create?
Capybara author jnicklas confirmed here that Capybara cannot make an app do things that are not available from the UI. He recommends controller tests for authorization.
However request specs written in RSpec without using Capybara syntax do allow direct use of HTML verbs (and some additional helpers) as outlined in the RSpec and Rails docs. So rather than Capybara's fill_in and click_link directives and the page object, you can use an attribute hash, verbs like get, post, post_via_redirect, and the response.body object. It's similar to a controller test, but you're using Rails' routing to choose the appropriate controller action based on the path provided. Here is an example of the latter technique:
describe "when standard user attempts to create account_admin user" do
let(:standard_user) { FactoryGirl.create(:standard_user) }
let(:attr) { { email: "account_admin#example.com",
password: "password",
password_confirmation: "password",
role: "account_admin" }
}
before do
login_as standard_user
get new_user_path
end
it "should not create a account_admin user" do
lambda do
post users_path, user: attr
end.should_not change(User, :count)
end
describe "after user posts invalid create" do
before { post_via_redirect users_path, user: attr }
# redirect to user's profile page
it { response.body.should have_selector('title', text: 'User Profile') }
it { response.body.should have_selector('div.alert.alert-error', text: 'not authorized') }
end
end
Pretty sure these tests are working correctly. Got them to fail by removing the dependent: :destroy options on the has_many :relationships and has_many :reverse_relationships in user.rb.
Wanted to share what I did in case anyone else is working through Michael Hartl's Rails Tutorial 2nd Edition, Chapter 11 Exercises.
A few questions arose from this exercise (see bottom of this post). If anyone could help, that'd be great.
Chapter 11, Exercise 1:
Add tests for dependent :destroy in the Relationship model (Listing 11.4 and Listing 11.16) by following the example in Listing 10.15.
Here's my test:
spec/models/user_spec.rb
require 'spec_helper'
describe User do
before do
#user = User.new(name: "Example User", email: "user#example.com",
password: "foobar", password_confirmation: "foobar")
end
subject { #user }
[...code omitted...]
describe "relationship associations" do
let(:other_user) { FactoryGirl.create(:user) }
before do
#user.save
#user.follow!(other_user)
other_user.follow!(#user)
end
it "should destroy associated relationships" do
relationships = #user.relationships
#user.destroy
relationships.should be_empty
end
it "should destroy associated reverse relationships" do
reverse_relationships = #user.reverse_relationships
#user.destroy
reverse_relationships.should be_empty
end
end
A couple questions arose from this exercise:
Question 1:
My initial tests were
relationships.should be_nil
reverse_relationships.should be_nil
But, realized an array was still being returned, despite no user existing.
So, when a user doesn't exist and an association method is called, the result is still an array? Is this always true?
Question 2:
I wanted to play around with deleting relationships and reverse_relationships for a user in the rails console.
I tried this
> user = User.first
> user.relationships
# returns a bunch of relationships
> user.relationships.destroy
=> []
> user.relationships
# returns same bunch of relationships
How do I actually destroy the relationships permanently? Seems like good thing to know when exploring in console.
Thanks! I'm still pretty new to Rails
may be you need smt like this
it { should have_many(:relationships).dependent(:destroy) }
I'm a ruby/rails noob too.
Question 1:
Searched rubyonrails.org for has_many and it says
Returns an array of all the associated objects. An empty array is returned if none are found.
On a side note, you can test for both nil and empty:
relationships.present?.should be_false
Question 2:
The user.relationships.destroy requires an :id
user.relationships.destroy '1'
Thanks for posting your code with your question. I only wanted to post this as a comment and not an answer, but it seems I can't yet. Anyway, I just wanted to add a small potential candidate to your tests, but from the other_user's perspective. The test is similar to the follow/unfollow tests, so hopefully it'd not too redundant, but it tests relationships directly and not the followed_users and followers that go through them.
describe "relationship associations" do
...
context "when a follower/followed user is destroyed" do
subject { other_user }
before { user.destroy }
its(:relationships) { should_not include(user) }
its(:reverse_relationships) { should_not include(user) }
end
end
Ruby on Rails Tutorial 2nd Edition.
Exercise 11.5.1 Add tests for destroying relationships associated with a given user.
This code works for me. I've tried to follow the example Listing 10.15.
spec/models/user_spec.rb
require 'spec_helper'
describe User do
before do
#user = User.new(name: "Example User", email: "user#example.com", password: "foobar", password_confirmation: "foobar")
end
subject { #user }
.
.
.
.
describe "user relationships associations" do
let (:other_user) { FactoryGirl.create(:user) }
let (:another_user) { FactoryGirl.create(:user) }
before do
#user.save
#user.follow!(other_user)
#user.follow!(another_user)
other_user.follow!(#user)
other_user.follow!(another_user)
another_user.follow!(#user)
another_user.follow!(other_user)
end
its(:followed_users) { should include(other_user) }
its(:followers) { should include(another_user) }
it "should destroy associated followers" do
followers = #user.followers
#user.destroy
followers.each do |follower|
follower.followed_users.should_not include(#user)
end
end
it "should destroy associated followed users" do
followed_users = #user.followed_users
#user.destroy
followed_users.each do |followed_user|
followed_user.followers.should_not include(#user)
end
end
end
end
Re: paul, the relationships array is not constituted by users, so his include() should always be false, so the test always green.
Re: maria, it appears that the followed_users and followers methods won't return a user who doesn't exist, even when if a relationship referencing he or she remains. So this test is never red also.
another solution:
describe "relationships" do
let(:other_user) { FactoryGirl.create(:user) }
before do
#user.save
#user.follow!(other_user)
end
let(:relationship) { #user.relationships.last }
describe "should be destroyed when the followed user is destroyed" do
before { other_user.destroy }
its(:relationships) { should_not include(relationship) }
end
describe "should be destroyed when the following user is destroyed" do
subject { other_user }
before { #user.destroy }
its(:reverse_relationships) { should_not include(relationship) }
end
end
The above answers work, but I figure I would share mine it's shorter.. :D
describe "following" do
let(:other_user) { FactoryGirl.create(:user) }
before do
#user.save
#user.follow!(other_user)
other_user.follow!(#user)
end
it { should be_following(other_user) }
its(:followed_users) { should include(other_user) }
it "should destroy associated followed_users and followers" do
#user.destroy
#user.relationships.present?.should be_false
#user.reverse_relationships.present?.should be_false
expect(other_user.followers).not_to include(#user)
expect(other_user.followed_users).not_to include(#user)
end
.
.
.
.
end
end
P.S you can leave out:
#user.relationships.present?.should be_false
#user.reverse_relationships.present?.should be_false
but I throw it in there for someone who wants to make sure that all associated destroy action is at work.