I have routes set up that look like:
match '/things/:thing_id' => "descriptions#index", :as => :thing
resources :things, :except => [:show] do
...
resources :descriptions, :only => [:create, :index]
end
How would I test the :create method for the nested descriptions?
So far i've got
context "with user signed in" do
before(:each) do
user = Factory.create(:user, :name => "Bob")
controller.stub!(:authenticate_user!).and_return(true)
controller.stub!(:current_user).and_return(user)
end
describe "PUT create" do
before(:each) do
#thing = Factory.create(:thing)
params = {"text" => "Happy Text"}
post thing_descriptions_path(#thing.id), params #Doesn't work
post :create, params #Doesn't work
end
end
end
The following should work:
describe "PUT create" do
before(:each) do
#thing = Factory(:thing)
attrs = FactoryGirl.attributes_for(:description, :thing_id => #thing)
post :create, :thing_id => #thing, :description => attrs
end
end
To create a nested resource you need to tell rspec the parent id in the post create so that it can insert it into the route.
You also need to create your :descriptions factory with the relationship to :thing built in, and also pass the thing_id into the :description attribute creation, this is to make sure that Factory Girl doesn't go and create a new :thing when it creates the attributes for :description, while this wouldn't cause the test to fail it would slow it down as you would end up creating two instances of :thing.
Related
I'm new to programming and have been learning Ruby on rails for about 11 weeks.
When running a feature spec (using RSpec and Capybara) on a simple "To do" list app. I get this error:
Failures:
1) User creates ITEM Successfully
Failure/Error: click_button 'Save'
ActionController::RoutingError:
No route matches [GET] "/items"
Here is my routes file:
devise_for :users
resources :items, only: [:new, :create, :show]
get 'about' => 'welcome#about'
root to: "welcome#index"
end
...here is the "items" controller:
class ItemsController < ApplicationController
def new
end
def show
end
def create
#item = Item.new(item_params)
#item.save
redirect_to #item
end
private
def item_params
params.require(:item).permit(:body)
end
end
the item model:
class Item < ActiveRecord::Base
belongs_to :list
scope :unfinished, -> { where('done' => false) }
scope :unfinished_and_recent, -> { unfinished.where("created_at > ?", Time.now-7.days) }
scope :finished, -> { where('done' => true) }
validates :body, length: { minimum: 5 }, presence: true
validates :list, presence: true
end
the spec:
require 'rails_helper'
feature 'User creates ITEM' do
scenario 'Successfully' do
visit new_item_path
fill_in 'Body', with: 'washcar'
click_button 'Save'
end
end
and the item factory:
FactoryGirl.define do
factory :item do
body 'itembody'
list
end
end
...and here is the form:
<h1>Items#new</h1>
<%= form_for Item.new do |form| %>
<%= form.text_field :body, placeholder: 'Body' %>
<%= form.submit 'Save' %>
<% end %>
i ran rake routes:
items POST /items(.:format) items#create
new_item GET /items/new(.:format) items#new
item GET /items/:id(.:format) items#show
about GET /about(.:format) welcome#about
Maybe try:
resources :items, only: [:new, :create, :show, :index]
If you run rake routes at the command line, it should show you the routes available for your app (and help explain the above).
Also, in case you haven't already read it:
http://guides.rubyonrails.org/routing.html
In my rails app I use devise for authenticating a user. I need to create rspec tests for a controller Arts which belongs_to User.
My Art model is the following:
class Art < ActiveRecord::Base
belongs_to :user
attr_accessible :description, :title, :image
has_attached_file :image, :styles => { :medium => "620x620>", :thumb => "200x200>" }
validates :title, :presence => true
validates :description, :presence => true
validates :image, :presence => true
end
In my ArtsController I have the following code:
class ArtsController < ApplicationController
before_filter :authenticate_user!
def create
#user = current_user
#art = #user.arts.create(params[:art])
end
end
I am trying to create a test to check if user is redirected to the sign in page when it tries to create an art and it is not logged in. So my test looks like this:
describe ArtsController do
describe "When user is not logged in" do
it "should be redirected to sign in page if creating new art" do
post :create
response.should redirect_to '/users/sign_in'
end
end
end
But I get the following error:
1) ArtsController When user is not logged in should be redirected to sign in page if creating new art
Failure/Error: post :create
ActionController::RoutingError:
No route matches {:controller=>"arts", :action=>"create"}
# ./spec/controllers/arts_controller_spec.rb:11:in `block (3 levels) in <top (required)>'
My routes.rb is like this:
Capuccino::Application.routes.draw do
devise_for :users
match "home" => "users#home", :as => :user_home
resources :users do
resources :arts
end
match "home/art/:id" => "arts#show", :as => :art
match "home/arts" => "arts#index", :as => :arts
end
How does my rspec test should be to perform this test?
You don't have any routes for arts#create that will take only the controller and action as parameters.
If you want to use your current nested route, you're gonna have to pass along the :user_id parameter in your request:
it "should be redirected to sign in page if creating new art" do
post :create, { :user_id => user_id }
response.should redirect_to '/users/sign_in'
end
But since you're trying to test the use case where you don't have a :user_id, you need a new non-nested route for that.
my routes.rb
namespace :magazine do
resources :pages do
resources :articles do
resources :comments
end
end
end
While writing controller specs for Comments:
describe "GET 'index'" do
before(:each) do
#user = FactoryGirl.create(:user)
#page = FactoryGirl.build(:page)
#page.creator = #user
#page.save
#article = FactoryGirl.create(:article)
#comment_attributes = FactoryGirl.attributes_for(:comment, :article_id => #article )
end
it "populates an array of materials" do
get :index, ??
#response.should be_success
assigns(:comments)
end
it "renders the :index view" do
get :index, ??
response.should render_template("index")
end
end
Any idea how to give the page and article reference to get :index ??
if I give : get :index, :article_id => #article.id
Error I get is below:
Failure/Error: get :index, :article_id => #article.id
ActionController::RoutingError:
No route matches {:article_id =>"3", :controller=>"magazine/comments"}
Your route requires at least two IDs: the comment's parent article, and the article's parent page.
namespace :magazine do
resources :pages do
resources :articles do
resources :comments
end
end
end
# => /magazine/pages/:page_id/articles/:article_id/comments
All parent IDs must be provided for this route to work:
it "renders the :index view" do
get :index, {:page_id => #page.id, :article_id => #article.id}
# [UPDATE] As of Rails 5, this becomes:
# get :index, params: {:page_id => #page.id, :article_id => #article.id}
response.should render_template("index")
end
With Rails 5 the params API changed:
get :index, params: { page_id: #page.id, article_id: #article.id }
https://relishapp.com/rspec/rspec-rails/v/3-7/docs/request-specs/request-spec#specify-managing-a-widget-with-rails-integration-methods
I spent most of the day trying to root out a problem with a controller spec, and the current workaround seems unacceptable to me. Any take on why this works? ... and what I should do instead.
Given a simple hierarchy as follows, and the following ability.rb, the properties_controller_spec.rb does not allow the spec below to pass without the line saying:
ability = Ability.new(subject.current_user)
Can you tell me why this would be?
Thanks!
Models:
class Account < ActiveRecord::Base
has_many :properties, :dependent => :nullify
end
class Property < ActiveRecord::Base
belongs_to :account
end
class User < Refinery::Core::BaseModel #for RefineryCMS integration
belongs_to :account
end
Ability.rb:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.has_role? :user
can [:read, :create, :update, :destroy], Property, account_id: user.account_id
else
can [:show], Property
end
end
end
properties_contoller_spec.rb:
require 'spec_helper'
describe PropertiesController do
def valid_attributes
describe "Authenticated as Property user" do
describe "PUT update" do
describe "with invalid params" do
it "re-renders the 'edit' template" do
property = FactoryGirl.create(:property, account: property_user.account)
# Trigger the behavior that occurs when invalid params are submitted
Property.any_instance.stub(:save).and_return(false)
ability = Ability.new(subject.current_user) # seriously?
put :update, {:id => property.to_param, :property => { }}, {}
response.should render_template("edit")
end
end
end
end
end
Arg! Found it myself.
Here it is:
config.include Devise::TestHelpers, :type => :controller
Following is the code to sign in the property_user, as directed by the Devise docs. (The locals in question are created in a global_variables.rb that is included. These are used all over the place.)
def signed_in_as_a_property_user
property_user.add_role "User"
sign_in property_user
end
def sign_in_as_a_property_user
property_user.add_role 'User'
post_via_redirect user_session_path,
'user[email]' => property_user.email,
'user[password]' => property_user.password
end
I've got two models:
class Solution < ActiveRecord::Base
belongs_to :user
validates_attachment_presence :software
validates_presence_of :price, :language, :title
validates_uniqueness_of :software_file_name, :scope => :user_id
has_attached_file :software
end
class User < ActiveRecord::Base
acts_as_authentic
validates_presence_of :first_name, :last_name, :primary_phone_number
validates_uniqueness_of :primary_phone_number
has_many :solutions
end
with my routes looking like this:
map.resources :user, :has_many => :solutions
Now I'm trying to test my solutions controllers with the following RSpec test:
describe SolutionsController do
before(:each) do
#user = Factory.build(:user)
#solution = Factory.build(:solution, :user => #user)
end
describe "GET index" do
it "should find all of the solutions owned by a user" do
Solution.should_receive(:find_by_user_id).with(#user.id).and_return(#solutions)
get :index, :id => #user.id
end
end
end
However, this gets me the following error:
ActionController::RoutingError in 'SolutionsController GET index should find all of the solutions owned by a user'
No route matches {:id=>nil, :controller=>"solutions", :action=>"index"}
Can anybody point me to how I can test this, since the index should always be called within the scope of a particular user?
Factory#build builds an instance of the class, but doesn't save it, so it doesn't have an id yet.
So, #user.id is nil because #user has not been saved.
Because #user.id is nil, your route isn't activated.
try using Factory#create instead.
before(:each) do
#user = Factory.create(:user)
#solution = Factory.create(:solution, :user => #user)
end
Looks like your other problem is on this line:
get :index, :id => #user.id
You're trying to make a request to the index method, but you've provided the wrong variable name. When testing SolutionsController id implies a solution id, you need to supply the user id. This should work, or at least move you forward:
get :index, :user_id => #user.id