rspec let method + loop - devise

i am having trouble running my rspec examples in a loop.
describe "GET all providers" do
let(:current_user) { Factory(:user) }
[:twitter, :facebook, :google_oauth2].each do |provider|
before :each do
current_user.confirm!
sign_in current_user
request.env['devise.mapping'] = Devise.mappings[:user]
request.env["omniauth.auth"] = OmniAuth.config.add_mock provider, {
:uid => '123456789',
:info => {
:email => current_user.email
}
}
end
it 'should add the authorization' do
get provider # method to create the authorization
authorization = Authorization.where(:provider => request.env["omniauth.auth"][:provider], :uid => request.env["omniauth.auth"][:uid]).first
current_user.authorizations.should include authorization
end
end
end
currently these examples all pass. the problem though is that current_user is a new user instance through each iteration of the loop, despite memoizing the current_user method. so if i add a test for current_user.authorizations.count.should == 3 it fails.
this became less of needing to actually test it, and more understanding why it isnt behaving how i expect. shouldn't let(:current_user) { Factory(:user) } persist the same user instance across all examples?

Here is a gist I came up with that might help you understand let and let!:
https://gist.github.com/3489451
let memoizes the return value within each it example, but executes the block every time it is called for the first time in the example.
For example, if you call current_user the first time in an it example block, the { Factory(:user) } block is executed. Calling current_user a second or any subsequent time within the same it example block does not execute the { Factory(:user) } block; the return value is memoized.

Related

Devise & Rspec: User requires account activation in requests spec even after confirmed

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

RSpec gives error 'trait not registered: name'

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.

Can't create a user factory in factory girl when using rspec, devise, guard, and spork

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 >.<

respond_with is asking for location on error

I have a pretty standard authenticate method
private
def authenticate_user
#current_user = User.find_by_authentication_token(params[:token])
unless #current_user
error = { :error => "Invalid token." }
respond_with(error, :status => 401 )
end
end
I am calling the API to ensure the authenticate fails.
I get an error stating
ArgumentError (Nil location provided. Can't build URI.):
app/controllers/api/v1/base_controller.rb:13:in `authenticate_user'
What am I doing wrong?
By the specific flavor of your error, I am guessing that "authenticate_user" is called as part of a "create" action.
If that is the case, I believe the answer I provided here will help you as well.
Assuming, however, that this is part of creating an authenticated session, meaning there is no actual location for the newly created "resource", I would supply nil for the response location, as in:
...
respond_with(error, :status => 401, :location => nil)
...
That will make more sense once you have a look at the linked answer. If it still doesn't make sense, I'll be happy to clarify.
I changed respond_with to render and it worked:
render json: { success: false, message: "an error" }, status: 500

Testing after_create hooks with rspec

I have code in my model (RoR 3.0.x) that is more or less like this:
class Message
after_create :notify
protected
def notify
if visible?
Notifier.message_from_portfolio( user, self ).deliver
else
Notifier.invisible_message_from_portfolio( user, self ).deliver
end
end
end
And I'm using the latest rspec gem to test it.
The problem is that I'm not able to test the notify method: if I test it directly I can't because it's protected, if I create a message and set expectations it doesn't work because apparently even though rspec runs the notify metod I'm not able to catch the calls in time.
My spec is:
describe :notification do
it "should send the whole message by email when visible" do
u = Factory.create( :user, :account_type => 1 )
message = u.messages.build( :body => "Whatever", :author => "Nobody", :email => "test#example.com" )
Notifier.should_receive( :message_from_portfolio )
message.save
end
end
The object Notifier never receives message_from_portfolio. What am I doing wrong? Suggestions?
Factory.create has already saved message, so it is not being created, just saved. Substitute it with Factory.build and all should be fine.
Are you sure the callback is being reached? after_create doesn't get executed if the instance is invalid.
You could set an expectation for debugging purposes:
message.should_receive(:after_create)
Or maybe visible? returns false? To check for that you could use a negative expectation:
Notifier.should_not_receive(:invisible_message_from_portfolio)