For my Rails 3 application I use FactoryGirl together with shoulda context (1.0.0.beta1) and matchers (1.0.0.beta3) for my functional tests.
My problem: in the code example below, the assign_to test fails because #user - to my surprise - turns out to be nil. In the outer setup block, #user is assigned a valid model instance, but from within the should assign_to statement the instance variable is not accessible. Why is that and what is the correct way to write that test?
class UsersControllerTest < ActionController::TestCase
context "as admin" do
setup do
#user = Factory.create(:user)
end
context "getting index" do
setup do
get :index
end
should assign_to(:users).with([#user])
end
end
I discovered that passing the value as a black miraculously works. However, after digging into the actual AssignToMatcher code, it doesn't seem to make sense why the parameter method wouldn't work while the block method would.
For the sake of the +250 rep I'm investing in this, I'd still like an answer that explains why the param method isn't working (and how to fix it), but until then, at least I have a workaround.
#dblp1, hopefully this works for you too. Here's an example specific to your code:
class UsersControllerTest < ActionController::TestCase
context "as admin" do
setup do
#user = Factory.create(:user)
end
context "getting index" do
setup do
get :index
end
should assign_to(:users).with { [#user] }
end
end
end
(I am pasting as an answer as it is fairly long)
I mocked a bit your test and checked what was the results passed.
Interestingly enough, when I call the matcher #post=null (as I believe in your case). The issues I think (after a while of investigation), it coming from the the order the setup do block is called and the fact that the variables defined in one context are not visible in the nested context.
modified class
context "as admin" do
setup do
#post = Post.new
puts "Step 1 - inside setup do"
puts #post.class
end
puts "Step 2 - outside setup do 1"
puts #post.class
context "getting index" do
setup do
get :index
end
puts "Step 3 - calling shoulda"
puts #post.class
should assign_to(:posts).with([#post])
#should assign_to(:posts).with { [#post] }
end
end
And the results in the console
ruby -I test test/functional/posts_controller_test.rb
Step 2 - outside setup do 1
NilClass
Step 3 - calling shoulda
NilClass
Loaded suite test/functional/posts_controller_test
Started
Step 1 - inside setup do
Post
So the setup cycle is called at the end (and not at the beginning) and then your is Nil when passed to the matcher.
Even if I remove the first setup do does not work pretty well.
Step 1 - inside setup do
Post
Step 2 - outside setup do 1
Post
Step 3 - calling shoulda
NilClass
Finally, putting the post in the inner context
Step 3 - calling shoulda
Post
If you call #user = Factory.create(:user) directly inside the "getting index" context, I believe it will work.
When you are working with indexes you should use the plural of the instance variable.
#users rather than #user
You should also populate it as an array.
Finally, Shoulda matchers should start with "it" and be contained in braces, at least in the RSpec world, which is what I use. Not sure if this is the case with Test::Unit or whether your formatting above will work.
Try something like this.
class UsersControllerTest < ActionController::TestCase
context "as admin" do
setup do
#user = Factory.create(:user)
end
context "getting index" do
setup do
#users = Array.new(3) { Factory(:user) }
get :index
end
it { should assign_to(:users).with(#users) }
end
end
Related
I'm relatively new to testing and very new to Rails 4 and rSpec. I am trying to test a controller that uses Devise for authentication and I am stuck. All of the examples I can find are for Rails 3.
I'm using Rails 4.0.3, Devise 3.2.3, rSpec 2.14.1 and FactoryGirl 4.4.0.
class LessonPlansController < ApplicationController
before_action :authenticate_user!
# GET /lesson_plans
def index
#lesson_plans = current_user.lesson_plans.to_a
end
.
.
.
private
# Use callbacks to share common setup or constraints between actions.
def set_lesson_plan
#lesson_plan = LessonPlan.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def lesson_plan_params
params[:lesson_plan]
end
def lesson_plan_params
params.require(:lesson_plan).permit(:title, :synopsis)
end
end
Here are my factory definitions: (Maybe I don't need to define user_id in the lesson_plan factory?)
FactoryGirl.define do
factory :user do
sequence( :username ) { |n| "user#{n}" }
sequence( :email ) { |n| "foo#{n}#example.com" }
password 'foobarbaz'
password_confirmation 'foobarbaz'
created_at Time.now
updated_at Time.now
end
end
FactoryGirl.define do
factory :lesson_plan do
user_id 1
title "The French Revolution"
synopsis "Background and events leading up to the French Revolution"
end
end
And the test part is where I get stuck.
describe LessonPlansController do
let(:valid_attributes) { { } }
let(:valid_session) { {} }
# describe "GET index" do
it "assigns all lesson_plans as #lesson_plans" do
user=FactoryGirl.create(:user)
sign_in user
lesson_plan = LessonPlan.create! valid_attributes
get :index, {}, valid_session
assigns(:lesson_plans).should eq([lesson_plan])
end
end
I'm not sure what to put in valid_attributes and valid_session (or if I even need them). The test will get as far as signing in the user, but will fail on creation of the lesson_plan. Admittedly this is the default/generated test for rSpec, but I am not sure how to proceed.
Examples I have seen use a before block to set up the user. I haven't been able to find anything on the Devise wiki page covering how to write basic rSpec tests for a controller that requires the user to be logged in. Any pointers would be greatly appreciated!
"I'm not sure what to put in valid_attributes and valid_session (or if I even need them)."
Well that depends what you're testing for.. Say you're testing validations & want to ensure that a record not be created if x column is set to null... then you could try to specifically create a record with invalid attributes (e.g. column: nil) and expect the result to not return true; maybe you want to ensure that it IS created with valid attributes.
You can btw, use `attributes_for(:factory_name)`` since you're using FactoryGirl. And no you don't necessarily need to specify the user's id in your lesson plan factory; unless you always want it to reference user 1. You can simply reference user with no value. Check out http://everydayrails.com/2012/03/12/testing-series-intro.html and especially parts 3-5 for an introduction to testing with RSPec.. I found this a pretty easy to follow guide when I was getting started.
I am developing this Rails 3.2 application using the Apartment gem as middleware. The application itself works perfectly and all the RSpec examples also work perfectly when ran individually. However, when I run all the tests at the same time using the bundle exec rspec command, there are two examples that fail in two different controller specs and they do exactly the same thing. Here are the two examples in question:
In the issues_controller_spec.rb file:
describe "GET 'new'" do
# ...
context "for authenticated users" do
before(:each) do
controller.log_in(create(:user))
get :new
end
# ...
it "should create a new issue instance and put it in an instance variable" do
assigns(:issue).should be_an_instance_of Issue
end
end
end
In the users_controller_spec.rb file:
describe "GET 'new'" do
# ...
context "for authenticated users" do
# ...
context "for admin users" do
before(:each) do
admin = create(:admin)
admin.add_role :admin
controller.log_in(admin)
get :new
end
# ...
it "should create a new User instance and put it in an instance variable" do
assigns(:user).should be_an_instance_of User
end
end
end
end
These two examples are affected by a before hook:
before(:each) do
client = create(:client)
#request.host = "#{client.account_name}.lvh.me"
end
When creating a new Client, there is an after_create callback:
# Create the client database (Apartment) for multi-tenancy
def create_client_database
begin
Apartment::Database.create(self.account_name)
rescue Apartment::SchemaExists
return
rescue
self.destroy
end
end
And there is where the examples fail. Now if I remove the begin...rescue...end block and keep the line Apartment::Database.create(self.account_name) I get the following exception in the failling examples:
ActiveRecord::StatementInvalid:
PG::Error: ERROR: current transaction is aborted, commands ignored until end of transaction block
: SET search_path TO public
Again, if I run the examples individually, they pass but if I run all the examples, the two examples above fail.
Does anyone know what I am doing wrong please?
Note: The whole application code can be found here.
I solved this problem by wrapping the line client = create(:client) in a begin, rescue, end block like so:
before(:each) do
begin
client = create(:client)
rescue
client = Client.create!(attributes_for(:client))
end
#request.host = "#{client.account_name}.lvh.me"
end
I don't know how or why this works but I know it works.
I am new to Rspec please tell me what would be the controller Spec for the following two methods In index method only login page is seen by entering the username control goes to login method and find the name of person. If person is find then control goes to people path otherwise it goes back to root path that is index page it self.
class HomeController < ApplicationController
def index
end
def login
#person = Person.find(:all, :conditions => ['people.name =?', params[:person][:name]] )
if #person.blank?
redirect_to root_path
else
redirect_to people_path
end
end
end
Please help me.
Thanks.
Your rspec controller tests could be like this:
describe HomeController do
render_views
it "Logs in Person with non-blank name" do
person = Factory(:Person, name: "non-blank name")
get :login
response.should redirect_to(people_path)
end
it "does not log in Person with blank name" do
person = Factory(:Person, name: "") # blank name
get :login
response.should redirect_to(root_path)
end
end
Refer to rails controller specs for details.
EDIT:
Factory: the code that creates objects (test objects in this case). This is a preferred method for creating test objects because you can customize your code to create objects with varying attributes with least duplication.
Fixtures: If you are not using factories, you can specify the attributes for each of the objects you are going to create. For more than 2-3 object, this data quickly becomes unmanageable to maintain (for example, when you add an attribute, you need to make changes for each of these objects).
Stubs: If you prefer not to create database records while creating model objects, you can stub the model code white testing controllers.
For more information, refer:
1. testing guide
2. asciicast (Note: this code refers to an older version of FactoryGirl gem. Refer below for up-to-date API of FactoryGirl)
3. FactoryGirl Readme
I'm new to RSpec and my controllers're using inherited_resources, I have this mock/stub setup like:
describe MarketsController do
def mock_market(stubs={})
#mock_market ||= mock_model(Market, stubs).as_null_object
end
describe "GET index" do
it "assigns all markets as #markets" do
Market.stub(:all){ [mock_market] }
get :index
assigns(:markets).should eql([mock_market])
end
end
end
And this spec fails because there's nothing in the assigns(:markets). After I added:
class MarketsController
def index
#markets = Market.all
end
end
it'll pass so I guess that's because the inherited_resources doesn't call Market.all to get all of the Market instance and thus bypass the stub for Market.stub(:all). The index method I added above is obviously redundant and shouldn't exist at all, so the question is, without call Market.all explicitly, what should I do in my spec to complete the tests? Thanks in advance!
If I am reading the code correctly, inherited_resources first tries to use Market.scoped if it exists. So do you have a scoped scope?
I am currently in the process of migration to rails 3 from rails 2 in a large application. In our functional specs, we have alot of stuff like this:
#model = Factory :model
#child = Factory :child
Model.stub!(:find).and_return(#model)
Child.stub!(:find).and_return(#child)
...
#child.should_receive(:method).twice
The main issue is that if I let it hit DB and get actual instance of child, real :method makes tests too complex (need two big factories) and slow.
In code we use various ways to get items: find, dynamic finders, etc
#model = Model.find(1)
#child = #model.children.find_by_name(name)
How would you advice to move this logic to rails 3? Any advice on another stubbing/mocking library maybe?
Normally you would mock the model inside controller specs:
Model.stub!(:find).and_return(mock_model('Model'))
Child.stub!(:find).and_return(mock_model('Child'))
However, when you've got gem "rspec-rails", "~> 2.0" in your rails 3 app's Gemfile, then the standard rails scaffold generator will use rspec to generate specs for you, so running rails generate scaffold MyResource will generate some example specs for you.
The following is a lightly annotated version of what rails/rspec will generate for controller specs, so I suppose this should be considered "The RSpec Way".
describe AccountsController do
# Helper method that returns a mocked version of the account model.
def mock_account(stubs={})
(#mock_account ||= mock_model(Account).as_null_object).tap do |account|
account.stub(stubs) unless stubs.empty?
end
end
describe "GET index" do
it "assigns all accounts as #accounts" do
# Pass a block to stub to specify the return value
Account.stub(:all) { [mock_account] }
get :index
# Assertions are also made against the mock
assigns(:accounts).should eq([mock_account])
end
end
describe "GET show" do
it "assigns the requested account as #account" do
Account.stub(:find).with("37") { mock_account }
get :show, :id => "37"
assigns(:account).should be(mock_account)
end
end
describe "GET new" do
it "assigns a new account as #account" do
Account.stub(:new) { mock_account }
get :new
assigns(:account).should be(mock_account)
end
end
end