Testing member routes of a resources in rails3 with rspec - ruby-on-rails-3

I'm creating an application that will allow the user to take an exam. The exam object has_many questions and I want the user to take the exam in two parts. In order to implement this workflow, I've created the following routes in config/routes.rb:
resources :exam do
member do
get 'get_part1'
put 'put_part1'
get 'get_part2'
put 'put_part2'
end
end
So, when the user issues GET /exam/:id/get_part1 they are shown the first set of questions, etc... I have all of this working and now I'm trying to write tests for it - I know that's backwards but it took me a while to figure out complex forms and stuff. I want to test that you cannot access the exam controller unless you are a signed in user. This is straight forward for new and create but I'm having trouble figuring out how to test the nested members. Here's what I've tried so far:
before(:each) do
#exam = Exam.create
end
it "should deny access to 'get_part1'" do
get get_part1_exam_path(#exam)
response.should redirect_to(signin_path)
end
However, this test fails with the following error:
Failure/Error: get get_part1_exam_path(#exam)
ActionController::RoutingError:
No route matches {:controller=>"exams", :action=>"/exams/1/get_part1"}
Any help would be greatly appreciated. Thanks!

Try that one:
get :get_part1, :id => #exam (or #exam.id)

Related

New to Rails 4 Testing - Need help getting started (rSpec and Devise)

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.

Rspec, FactoryGirl unable to find ActiveRecord method

I am trying to learn Rspec in a very simple CRUD Rails 3.2.8 app. I'm following the general pattern of Michael Hartl's examples and have been moderately successful with cucumber for the outside in portion. Now I want to test a Twilio SMS feature and cannot seem to get to first base, mostly because I'm not asking the right questions, so I expect to be corrected here and get back on track.
My app has two models, commodity and price and they interact with each other in my cucumber tests, so it appears. I'm aware, like in cucumber, I need an object to start to test its interactions. In my prices controller, I see that I can get the commodity's prices with the below in my prices#create method:
#price = #commodity.prices.build(params[:price])
I ultimately want to generate a factory that will have many prices for a given commodity. But I want to get to base first. Following thoughtbot's examples on their Readme I'm attempting the following in rails console:
FactoryGirl.create(:commodity) do |price|
Commodity.prices.build(attributes_for(:price))
end
The result is: NoMethodError: undefined method `prices' for #
Hmm, I must not be understanding either Rspec or Factory Girl. Here is my basic factories.rb:
FactoryGirl.define do
factory :commodity do
name "corn"
end
sequence :price do |n|
price
date { Time.now }
end
end
Here are my two models:
class Commodity < ActiveRecord::Base
attr_accessible :description, :name
has_many :prices
end
MOST_RECENT = 5
class Price < ActiveRecord::Base
attr_accessible :buyer, :date, :price, :quality, :commodity_id
scope :most_recent, lambda { order("id desc").limit(MOST_RECENT) }
belongs_to :commodity
end
My attempt to understand this is to do it simply in Rails console but the error also appears when I run rspec as well. But why would FactoryGirl, or Rspec, not seem to use the prices method I get with Active Record? Clearly, I'm not understanding something or I would have found the answer on Stack, thanx, sam
In your FactoryGirl.create there are a couple problems. First, the block argument should be commodity, not price. create passes the created object into the block. Second, you're trying to run prices on the Commodity class. In your object relationship, prices is an accessor associated with a specific instance. There is no Commodity#prices method, but any given instance of Commodity will have prices. You can probably use build like that, but I think the canonical way is to use the shift operator to add a Price.
Putting this together gets you:
FactoryGirl.create(:commodity) do |commodity|
commodity.prices << FactoryGirl.create(:price, commodity: commodity)
end
I'm not sure what you're doing with the sequence in your Commodity factory definition. If you're trying to make sure that Commodities are created with Prices by default (without adding them as above), check out some of the tips at http://icelab.com.au/articles/factorygirl-and-has-many-associations/.

Rspec controller spec

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

Nested Routing for Single Table Inheritance model rails 3.1

I created a Single table inheritance model in my model file and am having difficulty with the routing. When I use :as in my resource, it renames my named path.
Model file:
class Account < ActiveRecord::Base
belongs_to :user
end
class AdvertiserAccount < Account
end
class PublisherAccount < Account
end
Routes.rb
resources :advertiser_accounts, :as => "accounts" do
resources :campaigns
end
I used :as in my routes because it is a single table inheritance and I want to pass the account_id and not the advertiser_account_id. My link is http://127.0.0.1:3000/advertiser_accounts/1/campaigns
/advertiser_accounts/:account_id/campaigns/:id(.:format)
However, using :as renames my named path from advertiser_account_campaigns to account_campaigns. My route looks like
account_campaigns GET /advertiser_accounts/:account_id/campaigns(.:format) campaigns#index
So when I create a new item using form_for, I would get "undefined method `advertiser_account_campaigns_path'"
Edited: current hacked solution
A hack around way that I am using is to duplicate the code in the routes file. Anyone have suggestions?
resources :advertiser_accounts, :as => "accounts" do
resources :campaigns
end
resources :advertiser_accounts do
resources :campaigns
end
If you run "rake routes" with your setup you'll see this:
account_campaigns GET /advertiser_accounts/:account_id/campaigns(.:format) campaigns#index
POST /advertiser_accounts/:account_id/campaigns(.:format) campaigns#create
new_account_campaign GET /advertiser_accounts/:account_id/campaigns/new(.:format) campaigns#new
edit_account_campaign GET /advertiser_accounts/:account_id/campaigns/:id/edit(.:format) campaigns#edit
account_campaign GET /advertiser_accounts/:account_id/campaigns/:id(.:format) campaigns#show
PUT /advertiser_accounts/:account_id/campaigns/:id(.:format) campaigns#update
DELETE /advertiser_accounts/:account_id/campaigns/:id(.:format) campaigns#destroy
accounts GET /advertiser_accounts(.:format) advertiser_accounts#index
POST /advertiser_accounts(.:format) advertiser_accounts#create
new_account GET /advertiser_accounts/new(.:format) advertiser_accounts#new
edit_account GET /advertiser_accounts/:id/edit(.:format) advertiser_accounts#edit
account GET /advertiser_accounts/:id(.:format) advertiser_accounts#show
PUT /advertiser_accounts/:id(.:format) advertiser_accounts#update
DELETE /advertiser_accounts/:id(.:format) advertiser_accounts#destroy
So you should use "account_campaingns_path" in this setup, the ":as" actually changes the calls in the code not the paths in the url. If you want to change the paths you should use ":path =>" rather than ":as =>".
The Rails guide on routing also shows some examples with ":as" and ":path" and the resulting paths and helpers, you'll need to search a bit because think they only use in in examples explaining other cases.
Edit: rereading your question, I think you may also want to look at member routes, I'm not sure if that's what you want to mean with it being a single inheritance and not wanting to pass the advertiser_account's ':account_id'?

Show the attribute of one model on the show page of another model?

I have the following def in the client model:
def cli_full_name
[f_name, mi, l_name].join(' ')
end
I would like cli_full_name to display in the contract show page. Here is my app/views/contracts/show.html.haml page:
- provide(:title, #contract.authnum)
%h3
= #contract.authnum
%span1
= #client.cli_full_name
I get an error, "undefined method" cli_full_name.
The association is that contract has a *has_many :clients :through => :clientlines*
So I added a nested route to my routes file thinking that this would get things to work as follows:
resources :contracts do
resources :clients
end
No luck.
Can someone please help me understand how to get the cli_full_name to display on the contract show page?
Thanks.
Update
It works if you run over to the client show page but I want it on the contract show page:)
Even if I change my nested routes to the format proposed by Jamis Buck:
http://weblog.jamisbuck.org/2007/2/5/nesting-resources
I still cannot get cli_full_name to display on the contract show page.
Maybe I do not understand nested attributes and nested routes?
Help?
Because your model specifies that a contract has many clients you need to access them like this
#contract.clients
and then loop through them similar to
- #contract.clients.each do |client|
= client.cli_full_name