is there any way you can load some factories for all controller tests? I've got a few of them which are necessary for all controller tests (menu items) and I don't like putting them all in controllers seperately. Not very DRY :)
Thanks
Maybe you can take a look at rspec's shared context, highlighted here: https://www.relishapp.com/rspec/rspec-core/v/2-11/docs/example-groups/shared-context
Create an RSpec macro.
Using user as the Factory you want to use, here's an example you'd put in spec/support/controller_macros.rb:
module ControllerMacros
let!(:user) { FactoryGirl.create :user }
end
In your spec/spec_helper.rb include the macro for controllers:
RSpec.configure do |config|
config.extend ControllerMacros, :type => :controller
end
The let! is like let as if it was referenced in a before block.
The ControllerMacros is also a handy place to add things like a sign_in method, etc.
You can also put shared examples in spec/support as well.
Related
I am using devise. In a view partial, I have:
<%= simple_form_for resource, :as => :user, ...
In my application helper:
# #see http://stackoverflow.com/questions/4541075/what-is-the-devise-mapping-variable-and-how-can-i-include-it
def resource_class
devise_mapping.to
end
# #see http://stackoverflow.com/questions/4081744/devise-form-within-a-different-controller
def resource_name
:user
end
def resource
# with some controllers (homepage, static, this method is called)
# with other controllers (Item, it's not called: I don't see the abort call)
abort('should stop here')
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
On some controllers, my form works fine. On others, I get this error:
Couldn't find Item without an ID
The problem is that the method I have defined is not called. With some controllers, ApplicationHelper.resrouce is called, on others, it's an other method resource (Item.resource ?), and I don't know where it's defined.
How the method resource can be defined elsewhere ? Why is the method resource of my application helper not called on certain pages ?
Note: I am using the gem inherited_resources that defines the method resource. Maybe this create the conflict. But I don't know how the method is inherited
After seeing http://railscasts.com/episodes/230-inherited-resources, it all became clear.
I defined resource in ItemsController:
class ItemsController < InheritedResources::Base
# #see http://stackoverflow.com/questions/16831095/devise-the-method-resource-is-not-called-properly
def resource
# this solves my problem. It looks super wrong tough
#resource ||= User.new
#view_context.resource # tried that, but doesn't work
end
# ...
And now it works. The method resource was being re-defined by the gem for that controller...
I am using Rspec, FactoryGirl and Spork for my tests.There are 2 things I am a litte unclear on, first is the location of my factories.rb file. At present I have it located in
spec/support/factories.rb
And it looks like this
FactoryGirl.define do
factory :user do
email "example#yahoo.com"
password "password"
password_confirmation "password"
confirmed_at Time.now
end
end
Within my spec_helper I have
config.include FactoryGirl::Syntax::Methods
Secondly I want to login a user before starting my tests for a controller , this particular controller has a before filter :authenticate_user!
I am using devise for my authentication so have added
config.include Devise::TestHelpers, :type => :controller
Reading the devise docs you can add a controller_macros.rb and specify methods like so to use
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
sign_in user
end
end
And so i added this also to my spec_helper
config.include ControllerMacros, :type => :controller
So when I add login_user before my controller tests i get undefined method login_user. Am i using two tools here to do the same thing? Do I actually need the devise methods or can it all be done with factoryGirl. If so how do i setup the login process before i can test a controller?
Factories location should be in spec/factories. Check out this example app https://github.com/RailsApps/rails3-devise-rspec-cucumber/tree/master/spec.
For login, generally you seems to doing it right. Check the example app again and here: https://github.com/plataformatec/devise/wiki/How-To:-Controllers-and-Views-tests-with-Rails-3-%28and-rspec%29
For the undefined method login_user error be sure to have
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
and
config.extend ControllerMacros, :type => :controller
in spec_helper. Devise methods should be available wtih subject:
subject.current_user.should_not be_nil
Scenario
I have a controller with two actions only - :create and :delete. Where the create action is defined thus:
def create
# some code...
if #thing.save
redirect_to :back, :notice => "Successfully created."
else
redirect_to :back, :notice => "Successfully deleted."
end
end
I link to the action using...
<%= link_to "Become a friend", things_path(...), :method => :post %>
...in the view.
Problem
This works fine as I interact with the app in my browser. However, I wish to test this functionality using RSpec integration testing using Webrat's helper method - click_link "Become a friend" - which I think is correct. But I get this error
Failure/Error: click_link "I like Person-1's taste"
AbstractController::ActionNotFound:
The action 'index' could not be found for ThingsController
I can create an empty index action in the Things controller but this would violate the KISS Principle.
Questions
How can I workaround/fix this? And are there any best practices for cases like this?
The problem is that :method => :post makes rails create a form which is then submitted when you click the link. This only works with javascript enabled which webrat does not support out of the box. Have a look at https://github.com/brynary/webrat/wiki and try to get the test running with selenium. As selenium actually uses a real browser, your specs should run.
I'm having problems displaying data from a separate controller. I have a number of users, each with many pages. I've followed this tutorial with a few minor adjustments.
The error that keeps appearing is:
NoMethodError in SitesController#show
undefined method `page' for #<ActionDispatch::Request:0x00000102452d30>
My routes.rb is as follows:
devise_for :users
resources :users, :only => [:index, :show] do
resources :pages, :shallow => true
end
match '/' => 'sites#show', :constraints => { :subdomain => /.+/ }
root :to => "home#index"
And I have a sites controller:
class SitesController < ApplicationController
def show
#site = Site.find_by_name!(request.page)
end
end
I've also tried:
def show
#site = Site.find_by_name!(params[:site])
end
Which gives a different error.
Am totally stuck trying to figure this out!
Looking forward to your assistance.
Bob
The problem is here: request.page
The request object is of the class ActionDispatch::Request, which does not have a page method.
To track down errors like this, you can try either looking at the docs or messing around in the debugger.
Try running your controller with --debugger enabled.
If you are running Ruby 1.8, install the ruby-debug gem.
If you are running Ruby 1.9, install the ruby-debug19 gem.
Add a debugger call here:
class SitesController < ApplicationController
def show
debugger
#site = Site.find_by_name!(request.page)
end
end
Run your server with the --debugger option.
See what p request.page does. I bet it will have an "undefined method" error, just you see when you try to view that controller action.
If you do a p request.class you can find out what class the object is, and then look up the docs to see how to use it.
I'm refactoring some specs, in controller specs I have a before(:each) which sets up things required in the session, wanted to avoid duplication and put the initial setup global for each controller spec
my before filter is...
config.before(:each, :type => :controller) do
#...
session[:current_user] = #user
session[:instance] = #instance
#...
end
#user and #instance are also set in the before(:each)
i've just hidden them for readability here
I get the following error when running the controller tests
undefined method `session' for nil:NilClass
I would expect the global before callbacks to have the same things as the ones in the individual tests but I guess maybe they are loaded before the rails environment has been initialised?
Thanks
Use controller.stub!(:session, { :current_user => #user, :instance => #instance })
I think this is more to do with Ruby and blocks (which are closures).
The block passed to before is bound to the context in which it is created, and session is not available in that context.