When I run bundle exec rspec spec/ I have one of my tests fail that should be passing. Here's the error:
Failure/Error: #user = Factory(:user)
NoMethodError:
undefined method `Factory' for #<RSpec::Core::ExampleGroup::Nested_2::Nested_2:0x1037c0a70>
# ./spec/controllers/users_controller_spec.rb:21
Here's the test code:
describe "GET 'show'" do
before(:each) do
#user = Factory(:user)
end
it "should be successful" do
get :show, :id => #user
response.should be_success
end
it "should find the right user" do
get :show, :id => #user
assigns(:user).should == #user
end
end
I installed the Factory Girl gem, so I'm unsure what the issue is.
You say you installed factory_girl, but is it in both your Gemfile and Gemfile.lock files? (You may need to bundle install to get it into Gemfile.lock). Since you're using bundle exec, bundler will check your Gemfile.lock for the gems it should load.
[Edit]
On second thought, this looks like a duplicate of this SO question.
This may be not the case, but have you tried restarting the web server ?
The answers in this did not help me. What I found to solve my problem was to add a line to spec_helper.rb
require 'factory_girl'
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
end
Then in my spec's I would call it like so
person = build(:person)
Hope that helps someone. Was looking for about 3 hours.
I think, that this is a correct solution:
#user = Factory(:user)
Replace to
#user = Factory.create(:user)
Related
I found that there are not enough posts about this subject so I decide to post the question ones again.
I am simply trying to test my controllers that use devise with rspec .
I did add in rails_helper.rb
config.include Devise::TestHelpers, :type => :controller
I wrote this test:
describe AlbumsController do
render_views
let(:user) { User.create!(email: "rspec#example.com", password: "password") }
before(:each) do
sign_in(user)
end
it "should have a current_user" do
subject.current_user.should_not be_nil
end
describe "get ALBUM index" do
describe "for a registred user" do
it "should => index page" do
get :index
response.should have_selector('div#photos')
end
end
end
end
and my index method is really executed! I added some puts to follow the execution.
The subject.current_user is not null. everything should be good.
But I get a message:
<html><body>You are being redirected.</body></html>
There is No particular redirect_to or render method in my index action. It just render the default.
What is the problem here?
I'm using gem 'devise', "2.2.8" and gem 'rails', "3.2.1". It is old I know. I try to add tests before the migration to Rails 4 :-/
EDIT 1:
The current_user is valid in my index method.
It means it is not a Devise problem. but the view that is rendered
Try to use such construction
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in user
end
Source: https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-%28and-RSpec%29
Try adding:
before { request.env['HTTPS'] = 'on' }
I am having some trouble controller testing a rails engine that I created. The issue I am having is I can't seem to get rspec to use main_app routes.
For instance, I am writing the following controller test. The test would pass if it wasn't for the main_app.root_path returning nil.
The code I have in my controller spec is:
context "successful session creation" do
let(:user) {FactoryGirl.create :user}
it 'sets user id session' do
get :create, use_route: :authenticatable, email: user.email, password: 'password'
expect(session[:user_id]).to eq user.id
end
end
end
In my controller I have:
def create
user = Authenticatable::User.find_by(email: params[:email])
if user && user.login(params[:password])
session[:user_id] = user.id
redirect_to main_app.root_path, notice: "You are signed in!"
else
flash[:error] = 'Invalid email or password'
redirect_to authenticatable.sign_in_path
end
end
The rspec failure I get is:
NameError:
undefined local variable or method `main_app' for #<RSpec::ExampleGroups::AuthenticatableSessionsController::GETCreate:0x007f9f583d6f18>
How can I make it so that rspec will use all of the route helpers available to me.
My rails_helper.rb file is as follows:
ENV["RAILS_ENV"] ||= 'test'
require 'spec_helper'
require File.expand_path("../dummy/config/environment.rb", __FILE__)
require 'rspec/rails'
require 'factory_girl_rails'
require 'shoulda-matchers'
require 'pry'
require 'database_cleaner'
Rails.backtrace_cleaner.remove_silencers!
# Load support files
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
Thanks in advance for any help or suggestions.
It looks like there are otherswith similar problems ... I didn't have a chance to try this out myself but take a look at.
stack overflow
def main_app
Rails.application.class.routes.url_helpers
end
main_app.root_path
just put it directly in you controller test file or in rails helper, just like a separate method
I recently upgraded the RSpec version of my Rails 4 app. This is my setup:
# /Gemfile:
source 'https://rubygems.org'
group :development, :test do
gem 'rspec-rails', '~> 2.99'
...
end
# /spec/controllers/companies_controller_spec.rb:
require 'spec_helper'
describe CompaniesController, :type => :controller do
before(:all) do
#user = FactoryGirl.create(:user)
sign_in(#user)
end
describe 'GET #new' do
it "renders the :new view" do
get :new
expect(response).to render_template :new
end
end
...
end
# /spec/support/utilities.rb:
def sign_in(user)
cookies[:remember_token] = user.remember_token
end
The problem is that most of my tests are now failing and I've been trying to fix it for hours, but to no avail.
This is the error I get for nearly all tests that require a user sign in:
Failure/Error: sign_in(#user)
NoMethodError:
undefined method `cookie_jar' for nil:NilClass
# ./spec/support/utilities.rb:2:in `sign_in'
The #user object gets created by FactoryGirl, however. I've checked that in the test database and in the logs.
Why is my method for signing in users not working?
It worked like a charm before upgrading to a later version of RSpec.
Thanks for any help.
In a before(:all) you shouldn't be doing stuff that only makes sense in the context of a single example.
In particular your sign_in method fiddles with the cookies, which is implicitly the current request/controller's cookies. This doesn't make sense in a before(:all) since each example is supposed to have a separate request - the exception occurs because there is no current request. Change the before(:all) to a before(:each) and you should be ok.
Rspec used to be more lenient in this respect, but 2.99 tightened this up, deprecation warnings were also added for similar uses in some of the earlier 2.x versions (if you had depreciations about using let/subject in a before(:all) block, that was it)
I use:
gem 'rails', '3.2.11'
gem 'rspec-rails', '2.13.2'
gem 'webrat', '0.7.3'
gem 'factory_girl_rails', '4.1.0'
gem 'spork', '~> 0.9.0.rc'
I want to test my HP where I always have a link to a certain user, so the pages controller for HP contains:
#user = User.find(7)
And the view HP contains:
link_to 'See user', #user
The problem is that all tests fail since test database has no user with id 7. I tried:
FactoryGirl.define do
factory :user do
name "Testuser"
id "7"
end
end
... but this doesn't work. There is always the error:
The spec is this:
describe "GET 'home'" do
before(:each) do
FactoryGirl.create(:user)
end
it "should be successful" do
get 'home'
response.should be_success
end
end
Failure/Error: get 'home'
ActiveRecord::RecordNotFound:
Couldn't find User with id=7
The HP is working fine in reality, just the test fails. How can I assure this test is not going to fail?
Based on the Using Factories section of the documentation, I would set the id in the spec itself with something like:
#user = FactoryGirl.create(:user, :id => 7)
What about mocking / stubbing (much faster spec when not saving objects to database):
let(:user) { FactoryGirl.build(:user) }
let(:id) { '1' }
before do
user.stub(:id).and_return(id) # just to illustrate, not necessary if stubbing the find class method
User.stub(:find).and_return(user)
end
it { expect(user.id).to eq(id) } # just to illustrate
it { expect(response).to be_success }
it { expect(assigns(:user)).to eq(user) }
If you still have issue with the user being only instantiated, you can still use the above technique but replace FactoryGirl.build(:user) by FactoryGirl.create(:user)
Not an answer but a suspicition... you probably shouldn't write the spec the way you are doing it.
in your spec do something like:
u=FactoryGirl.create(:user)
User.where('id=?',u.id).count.should == 1
Making your tests dependent on specific ids is a recipe for disaster.
You can set the ID of a user by doing something like
before
u = FactoryGirl.create(:user)
u.update_attribute(:id, 7)
end
However, this is a little odd. You may even run into a case where there are user's 7.
What you could do though, is use Rspec's stubs to get the proper user. For instance you could do something like
let(:user_7) { FactoryGirl.create(:user) }
before { User.stub(:find).with(7).and_return(user_7) }
In your case I am sure you are just checking that the user id is being used instead of a specific ID.
When I write tests I just get the id returned from the database which is far more reliable and closer to what you are doing in reality.
I would write the controller spec something like this.
RSpec.describe MyController, type: :controller do
let(:user) { FactoryGirl.create(:user) }
before do
# login the user here which would resolve which user id is being used
sign_in user #assumes you are using warden test helpers
get :home
end
it "allows the user to the page" do
assert_response :ok
end
end
This should load fine. If you wish to double check that the link is correct you should do that in a feature spec using something like capybara.
I have been playing with Rails for a couple of years now and have produced a couple of passable apps that are in production. I've always avoided doing any testing though and I have decided to rectify that. I'm trying to write some tests for an app that I wrote for work that is already up and running but undergoing constant revision. I'm concerned that any changes will break things so I want to get some tests up and running. I've read the RSpec book, watched a few screencasts but am struggling to get started (it strikes me as the sort of thing you only understand once you've actually done it).
I'm trying to write what should be a simple test of my ReportsController. The problem with my app is that pretty much the entire thing sits behind an authentication layer. Nothing works if you're not logged in so I have to simulate a login before I can even send forth a simple get request (although I guess I should write some tests to make sure that nothing works without a login - I'll get to that later).
I've set up a testing environment with RSpec, Capybara, FactoryGirl and Guard (wasn't sure which tools to use so used Railscasts' suggestions). The way I've gone about writing my test so far is to create a user in FactoryGirl like so;
FactoryGirl.define do
sequence(:email) {|n| "user#{n}#example.com"}
sequence(:login) {|n| "user#{n}"}
factory :user do
email {FactoryGirl.generate :email}
login {FactoryGirl.generate :login}
password "abc"
admin false
first_name "Bob"
last_name "Bobson"
end
end
and then write my test like so;
require 'spec_helper'
describe ReportsController do
describe "GET 'index'" do
it "should be successful" do
user = Factory(:user)
visit login_path
fill_in "login", :with => user.login
fill_in "password", :with => user.password
click_button "Log in"
get 'index'
response.should be_success
end
end
end
This fails like so;
1) ReportsController GET 'index' should be successful
Failure/Error: response.should be_success
expected success? to return true, got false
# ./spec/controllers/reports_controller_spec.rb:13:in `block (3 levels) in <top (required)>'
Interestingly if I change my test to response.should be_redirect, the test passes which suggests to me that everything is working up until that point but the login is not being recognised.
So my question is what do I have to do to make this login work. Do I need to create a user in the database that matches the FactoryGirl credentials? If so, what is the point of FactoryGirl here (and should I even be using it)? How do I go about creating this fake user in the testing environment? My authentication system is a very simple self-made one (based on Railscasts episode 250). This logging in behaviour will presumably have to replicated for almost all of my tests so how do I go about doing it once in my code and having it apply everywhere?
I realise this is a big question so I thank you for having a look.
The answer depends on your authentication implementation. Normally, when a user logs in, you'll set a session variable to remember that user, something like session[:user_id]. Your controllers will check for a login in a before_filter and redirect if no such session variable exists. I assume you're already doing something like this.
To get this working in your tests, you have to manually insert the user information into the session. Here's part of what we use at work:
# spec/support/spec_test_helper.rb
module SpecTestHelper
def login_admin
login(:admin)
end
def login(user)
user = User.where(:login => user.to_s).first if user.is_a?(Symbol)
request.session[:user] = user.id
end
def current_user
User.find(request.session[:user])
end
end
# spec/spec_helper.rb
RSpec.configure do |config|
config.include SpecTestHelper, :type => :controller
end
Now in any of our controller examples, we can call login(some_user) to simulate logging in as that user.
I should also mention that it looks like you're doing integration testing in this controller test. As a rule, your controller tests should only be simulating requests to individual controller actions, like:
it 'should be successful' do
get :index
response.should be_success
end
This specifically tests a single controller action, which is what you want in a set of controller tests. Then you can use Capybara/Cucumber for end-to-end integration testing of forms, views, and controllers.
Add helper file in spec/support/controller_helpers.rb and copy content below
module ControllerHelpers
def sign_in(user)
if user.nil?
allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, {:scope => :user})
allow(controller).to receive(:current_user).and_return(nil)
else
allow(request.env['warden']).to receive(:authenticate!).and_return(user)
allow(controller).to receive(:current_user).and_return(user)
end
end
end
Now add following lines in spec/rails_helper.rb or spec/spec_helper.rb
file
require 'support/controller_helpers'
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.include ControllerHelpers, :type => :controller
end
Now in your controller spec file.
describe "GET #index" do
before :each do
#user=create(:user)
sign_in #user
end
...
end
Devise Official Link
The easiest way to login with a user on feature tests is to use the Warden's helper #login_as
login_as some_user
As I couldn't make #Brandan's answer work, but based on it and on this post, I've came to this solution:
# spec/support/rails_helper.rb
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } # Add this at top of file
...
include ControllerMacros # Add at bottom of file
And
# spec/support/controller_macros.rb
module ControllerMacros
def login_as_admin
admin = FactoryGirl.create(:user_admin)
login_as(admin)
end
def login_as(user)
request.session[:user_id] = user.id
end
end
Then on your tests you can use:
it "works" do
login_as(FactoryGirl.create(:user))
expect(request.session[:user_id]).not_to be_nil
end
For those who don't use Devise:
spec/rails_helper.rb:
require_relative "support/login_helpers"
RSpec.configure do |config|
config.include LoginHelpers
end
spec/support/login_helpers.rb:
module LoginHelpers
def login_as(user)
post "/session", params: { session: { email: user.email, password: "password" } }
end
end
and in the specs:
login_as(user)