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
Related
I'm using RSpec, FactoryGirl, and PhantomJS.
UPDATE:
I have verified that if I create this item in my spec/support/login_macros.rb login_user method, which I call below, the listing object is available in the view.
This seems like a FactoryGirl issue. Why can I create from a factory in that helper method, but can't inside the test itself? Here is the support method:
module LoginMacros
def login_user(admin = false)
myrepo = FactoryGirl.create(:repository, :myname)
plan = FactoryGirl.create(:plan, name: "Test Multi", listing_limit: 5000, repositories_allowed: 5)
user = FactoryGirl.create(:user)
subscription = FactoryGirl.create(:subscription, user: user, plan: plan)
user.add_repository(myrepo)
listing = FactoryGirl.create(:listing, user: user, repository: myrepo)
visit login_path
fill_in 'Email', :with => user.email
fill_in 'Password', :with => user.password
click_button 'Go'
user
end
I have set transactional_fixtures to false in my spec_helper.rb. Here is my spec:
require "spec_helper"
describe "App Integration" do
let!(:user) { login_user }
let!(:myrepo) { user.repositories.where(name: "myrepo" ).first }
it "lets a user add apps to a listing", js: true do
listing = FactoryGirl.create(:listing, user: user, repository: myrepo)
puts listing.inspect
Capybara::Screenshot.screenshot_and_open_image
end
end
Now here is the problem. See that puts line above? It prints out the object.
But in the screenshot, it's as if the object were never created. Like magic!
And yet, both the User and the Repository objects are visible in the view.
Also, I can go to a different view and see that Listing object. Just not on the main page of my application!
Why would that object be visible in one view and not the other? I'm just doing this on the main page:
<h3><%= Listing.count %></h3>
And it is always, always, always zero. Makes zero sense.
It doesn't look like you're ever calling visit inside of your test, so the page would always be blank. You need to call visit root_path # (or whatever path that heading is on) before saving the screenshot.
let! blocks are invoked before it blocks. Since you're viewing the page in a let! block but creating the listing in the it block, the listing isn't created until after you've viewed the page.
I tried to test my Rails 3 application on Windows with RSpec. I've wrote tests and factories, but can't solve the issues which raise when I run RSpec on command line.
Here is one of the test files:
require 'spec_helper'
describe "SignIns" do
it "can sign in" do
user = FactoryGirl.create(:user)
visit new_user_session_path
fill_in "login", with: user.username
fill_in "password", with: user.password
click_on "sign in"
current_user.username.should == user.username
end
end
And here's the factories.rb:
factory :layout do
name "layout1"
end
factory :club do
sequence(:name) { |i| "Club #{i}" }
contact_name "John Doe"
phone "+358401231234"
email "#{name}#example.com"
association :layout
end
factory :user do
sequence(:username) { |i| "user#{i}" }
password 'password'
email "test#example.com"
club
end
When I try to run RSpec it gives the following error:
trait not registered: name
#C: in 'object'
#.spec/features/sign_in_spec.rb:11:in 'block (2 levels) in (top(required))
What am I doing wrong?
I know this is an old question, but in case anyone else ends up here when searching "Trait not registered":
When using a dependent attribute like how email depends on name in the :club factory from the question, you need to wrap the attribute in curly braces so it gets lazy evaluated:
email {"#{name}#example.com"}
It's a FactoryGirl error, and it seems you're using (at spec/features/sign_in_spec.rb:11) something like :
FactoryGirl.create :user, :name
This will only work if you registered a trait called name for the Factory user, more on traits here
Note that if you just want to override the name of the created user, the syntax is
FactoryGirl.create :user, name: 'THE NAME'
For future readers' reference:
What didn't work -
ArgumentError:
Trait not registered: user_id
FactoryBot.define do
factory :startup do
user_id
name { FFaker::Lorem.word }
website { FFaker::Internet.uri(host: 'example.com') }
founded_at { "01.01.2000" }
end
end
How I solved this issue using either of these, when everything seemed to look right:
put empty curly braces after user_id
FactoryBot.define do
factory :startup do
user_id {}
name { FFaker::Lorem.word }
website { FFaker::Internet.uri(host: 'example.com') }
founded_at { "01.01.2000" }
end
end
Move user_id below other block-using helpers:
FactoryBot.define do
factory :startup do
name { FFaker::Lorem.word }
website { FFaker::Internet.uri(host: 'example.com') }
founded_at { "01.01.2000" }
user_id
end
end
Another late answer. I banged my head awhile because I forgot my model is very new and I didn't migrate the test database. So the attribute in fact did not exist.
i.e. had to run beforehand
rails db:migrate RAILS_ENV=test
In my case, none of the above(below?) answers helped to solve trait not registered error.
This time it was caused by improper order of loading factories. I've moved a file with declared global traits and FactoryBot started to load it in an improper order.
How did I fix it?
Rename it as, "spec/factories/01_factory_traits" and the issue is solved.
Perhaps there is a better way but it works.
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.