I have several controllers already set up. Now I want to start writing spec tests for them. Is there a command that generates the spec files automatically? I know rails does this for new resources, but I don't know if it does it for existing controllers/models too.
rails g rspec:controller ControllerName
When it asks you to override the existing controller, type n.
There are two options. If you want an empty spec file, you could try with:
rails g rspec:controller ControllerName
Now, if you want a spec file with initial specs for a basic REST controller, try with:
rails g rspec:scaffold ControllerName
If you've configured rspec in application.rb:
config.generators do |g|
g.test_framework :rspec
end
then rails g controller things will work. Opt not to overwrite files as they're generated.
All a spec looks like when it's generated is the following:
require 'spec_helper'
describe ThingsController do
it "should be successful" do
get :index
response.should be_successful
end
end
I often create the specs manually, as it's rather trivial.
Related
I'm attempting to use Devise (2.2.4), which I'm new to, with the Rails 3.2.13/Ruby 2.0.0p195 app I'm building. I turned scoped_views on because I want to have my own separate users and admins views. And I created my own Users::RegistrationsController which seems to be doing what I want it to. I've just added my own Users::SessionsController, which is where I've hit problems.
I straight copied over a couple of action methods from the Devise::SessionsController source as a first step, planning to modify them once they were working (my controller code is at the bottom of this post). But my 'new' method is failing, when called, with a NameError because `sign_in_params' is apparently undefined.
Well, that seems pretty strange because I'm inheriting from Devise::SessionsController, and when I look at the source for that on GitHub, there's the sign_in_params defined in the protected section at the bottom. So I decided to investigate whether my controller is inheriting correctly from Devise::SessionsController - and it certainly seem to be. I can list out all the inherited methods, just not that one missing one. So I ended up running the following piece of code in the Rails Console:
(Devise::SessionsController.new.methods - DeviseController.new.methods).each {|m| puts m}
And it produces the following output:
_one_time_conditions_valid_68?
_one_time_conditions_valid_72?
_callback_before_75
_one_time_conditions_valid_76?
new
create
destroy
serialize_options
auth_options
If I ignore the underscored methods, the remainder are all those methods defined in the Devise::SessionsController source except sign_in_params. I can't see how anything I've written can be deleting that method, and I can't think what else to try. Google is silent on this problem, so I assume I'm doing something uniquely foolish, but I can't work out what. Any suggestions please? And might someone else try running that bit of Rails Console code to see what they get?
class Users::SessionsController < Devise::SessionsController
prepend_before_filter :require_no_authentication, :only => [ :new, :create ]
prepend_before_filter :allow_params_authentication!, :only => :create
prepend_before_filter { request.env["devise.skip_timeout"] = true }
# GET /resource/sign_in
def new
self.resource = resource_class.new(sign_in_params)
clean_up_passwords(resource)
respond_with(resource, serialize_options(resource))
end
# POST /resource/sign_in
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => after_sign_in_path_for(resource)
end
end
I think you are using code from a devise version compatible with Rails 4 on a rails 3 application.
sign_in_params is a method to be used with strong parameters. A gem used in rails 4.
If you check the controller on devise version 2.2. https://github.com/plataformatec/devise/blob/v2.2/app/controllers/devise/sessions_controller.rb
You will see that there is no sign_in_params method.
Check which version of devise you are using and copy the code based on that devise version in your controller, rather than the latest code from github.
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
I'm writing a rails 3 railtie which defines some view helpers. Now I wonder how to spec the view helper methods because I cannot instantiate a module. I already searched and read a lot, but I just couldnt get it working.
view_helper_spec.rb
require 'spec_helper'
module MyRailtie
describe ViewHelper, :type => :helper do
describe "style_tag" do
it "should return a style tag with inline css" do
helper.style_tag("body{background:#ff0}").should ==
body{background:#ff0}
EOT
end
end
end
end
It always complains that "helper" is not defined. It seems that :type => :helper doesn't to anything.
The gemspec requires the rspec and the rspec-rails gems in development/ test mode.
I think I found the solution (it's painfully obvious once you think about it). Just stick this at the top of your spec:
require 'action_view'
require 'active_support'
include ViewHelper
include ActionView::Helpers
I am using the rails3-amf gem by warhammerkid in my Rails 3 / Flex 4 project.
AFAIK, I have correctly followed the "Getting Started" instructions from the GitHub page.
I have added the gem lines to my Gemfile.
I have installed the gems using bundle install.
From my Flex application, I will be making the RemoteObject call to the index action in the ManageMySchool::GradesController file. This is the code in the app/controllers/manage_my_school/grades_controller.rb file:
class ManageMySchool::GradesController < ApplicationController
respond_to :html, :amf
def index
#grade = Grade.first
respond_with(#grade) do |format|
format.amf { render :amf => #grade.to_amf }
end
end
end
The name of the model which is to be serialized is called Grade in both the Rails project (app/models/Grade.rb) and the Flex project (Grade.as with a RemoteAlias set as Grade). In the config/application.rb file, I have done the class mapping this way:
config.rails3amf.class_mapping do |m|
m.map :as => 'Grade', :ruby => 'Grade'
end
And I have done a parameter mapping this way:
config.rails3amf.map_params :controller => 'ManageMySchool::GradesController', :action => 'index', :params => [:authenticity_token]
Problem
Now, when I run the server and make the RemoteObject call from Flex, I get a to_amf undefined method error for the Grade model.
If I change Grade.first to Grade.all, #grade would have an array of Grades. But the undefined method error message still mentions the Grade model. This means that the to_amf method is working for the Array class but not for the ActiveRecord model.
Why is this? What am I doing wrong?
Is there something I have to do to "enable" the rails3-amf gem for ActiveRecord models?
I would appreciate any insights. Thanks!
Update
#warhammerkid: Here is the output of Grade.ancestors as seen in rails console.
ree-1.8.7-2011.03 :006 > puts Grade.ancestors
Grade
ActiveRecord::Base
Paperclip::CallbackCompatability::Rails3::Running
Paperclip::CallbackCompatability::Rails3
Paperclip::Glue CanCan::ModelAdditions
Authlogic::ActsAsAuthentic::ValidationsScope
Authlogic::ActsAsAuthentic::SingleAccessToken
Authlogic::ActsAsAuthentic::SessionMaintenance
Authlogic::ActsAsAuthentic::RestfulAuthentication::InstanceMethods
Authlogic::ActsAsAuthentic::RestfulAuthentication
Authlogic::ActsAsAuthentic::PersistenceToken
Authlogic::ActsAsAuthentic::PerishableToken
Authlogic::ActsAsAuthentic::Password
Authlogic::ActsAsAuthentic::MagicColumns
Authlogic::ActsAsAuthentic::Login
Authlogic::ActsAsAuthentic::LoggedInStatus
Authlogic::ActsAsAuthentic::Email
Authlogic::ActsAsAuthentic::Base
ActiveRecord::Aggregations
ActiveRecord::Transactions
ActiveRecord::Reflection
ActiveRecord::Serialization
ActiveModel::Serializers::Xml
ActiveModel::Serializers::JSON
ActiveModel::Serialization
ActiveRecord::AutosaveAssociation
ActiveRecord::NestedAttributes
ActiveRecord::Associations
ActiveRecord::AssociationPreload
ActiveRecord::NamedScope
ActiveModel::Validations::Callbacks
ActiveRecord::Callbacks
ActiveModel::Observing
ActiveRecord::Timestamp
ActiveModel::MassAssignmentSecurity
ActiveRecord::AttributeMethods::Dirty
ActiveModel::Dirty
ActiveRecord::AttributeMethods::TimeZoneConversion
ActiveRecord::AttributeMethods::PrimaryKey
ActiveRecord::AttributeMethods::Read
ActiveRecord::AttributeMethods::Write
ActiveRecord::AttributeMethods::BeforeTypeCast
#<Module:0x1028356f0> ActiveRecord::AttributeMethods::Query
ActiveRecord::AttributeMethods
ActiveModel::AttributeMethods
ActiveRecord::Locking::Optimistic
ActiveRecord::Locking::Pessimistic
ActiveRecord::Validations
ActiveModel::Validations::HelperMethods
ActiveModel::Validations
ActiveSupport::Callbacks
ActiveModel::Conversion
ActiveRecord::Persistence Object
PP::ObjectMixin Base64::Deprecated
Base64
ActiveSupport::Dependencies::Loadable
Kernel
Note that only ActiveModel::Serialization is there. No mention of Rails3AMF.
Does this mean I have to do something special to load the Rails3AMF module for the ActiveRecord models?
I am using Rails 3.0.5 with the latest version of ree. The gems are all contained in a gemset managed using rvm.
Update 2
If I remove the to_amf in the render :amf line, then I get the following error:
Grade Load (0.3ms) SELECT `grades`.* FROM `grades` LIMIT 1
Completed 200 OK in 195ms (Views: 0.1ms | ActiveRecord: 0.8ms)
Sending back AMF
NoMethodError (You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.last):
Rendered /Users/anjan/.rvm/gems/ree-1.8.7-2011.03#rb/gems/actionpack-3.0.5/lib/> > action_dispatch/middleware/templates/rescues/_trace.erb (1.1ms)
Rendered /Users/anjan/.rvm/gems/ree-1.8.7-2011.03#rb/gems/actionpack-3.0.5/lib/> > action_dispatch/middleware/templates/rescues/_request_and_response.erb (2.8ms)
Rendered /Users/anjan/.rvm/gems/ree-1.8.7-2011.03#rb/gems/actionpack-3.0.5/lib/> > action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (13.6ms)
Started POST "/amf" for 127.0.0.1 at Fri Apr 15 17:03:34 +0530 2011
Sending back AMF
Update 3
If I manually add the line include Rails3AMF::Serialization at the top of the Grade.rb model file, then it all works. So, does this mean that I have to put this line in all my models?
I see that this is already being done in line numbers 40 - 42 in the lib/rails3-amf/serialization.rb file of the gem:
# Hook into any object that includes ActiveModel::Serialization
module ActiveModel::Serialization
include Rails3AMF::Serialization
end
Why isn't this working? Should I force-load the gem when my application initializes or something?
Thanks!
Update 4 - Solved by this workaround
Okay, I just ended up adding this code block in an initializer:
class ActiveRecord::Base
include Rails3AMF::Serialization
end
And it is working.
#warhammerkid - Thanks for the help.
Rails3AMF::Serialization, the module that adds the to_amf method, is included in ActiveModel::Serialization when Rails3-AMF loads. If it's somehow not being included even though the code is running and ActiveModel::Serialization is one of your model's ancestors, then the simplest solution is just to add "include Rails3AMF::Serialization" at the top of your model implementation. I've never tried gem sets before, but it might be an issue with them, as everything works correctly using Bundler.
As an aside, feel free to post a bug to https://github.com/warhammerkid/rails3-amf/issues.
I'm fairly new to rails and TDD (as will no doubt be obvious from my post) and am having a hard time wrapping my brain around Rspec and FactoryGirl.
I'm using Rails 3, rspec and factory girl:
gem 'rails', '3.0.3'
# ...
gem 'rspec-rails', '~>2.4.0'
gem 'factory_girl_rails'
I have a user model that I've been successfully running tests on during development, but then needed to add an attribute to, called "source". It's for determining where the user record originally came from (local vs LDAP).
In my factories.rb file, I have several factories defined, that look something like the following:
# An alumnus account tied to LDAP
Factory.define :alumnus, :class => User do |f|
f.first_name "Mickey"
f.last_name "Mouse"
f.username "mickeymouse"
f.password "strongpassword"
f.source "directory"
end
I have a macro defined (that's been working up until now) that looks like this:
def login(user)
before(:each) do
sign_out :user
sign_in Factory.create(user)
end
end
I'm calling it in multiple specs like so (example from users_controller_spec.rb):
describe "for non-admins or managers" do
login(:alumnus)
it "should deny access" do
get :index
response.should redirect_to(destroy_user_session_path)
end
end
If I don't specify the "source" attribute, everything works OK, but as soon as I do, I get an error like so when running the test
12) UsersController for non-admins or managers should deny access
Failure/Error: Unable to find matching line from backtrace
NoMethodError:
undefined method `source=' for #<User:0x00000100e256c0>
I can access the attribute no problem from the rails console and the app itself, and it's listed in my attr_accessible in the user model. It's almost as though Rspec is seeing an old version of my model and not recognizing that I've added an attribute to it. But if I put the following line into my user model, the error disappears
attr_accessor :source
... which indicates to me that it is actually looking at the correct model.
Help!
How about running this?
rake db:test:load
[If you added a new attribute you'd need to migrate it to the test database.]
if you don't use schema.rb (e.g. you have set config.active_record.schema_format = :sql)
you should run
rake db:test:prepare