This one is really annoying, im probably just missing something silly. been at it for 2h now, can see whats the problem. error: ActiveModel::ForbiddenAttributesError:
order.rb
has_many :item_lines
accepts_nested_attributes_for :item_lines
order_controller.rb
def create
if params[:v1_order][:item_lines_attributes].present?
puts "attributes: "
puts params[:v1_order][:item_lines_attributes] # {"quantity"=>"1", "net_price"=>"9.99"}
#v1_order = V1::Order.new(v1_order_params)
#v1_order.item_lines.build(params[:v1_order][:item_lines_attributes])
else
#v1_order = V1::Order.new(v1_order_params)
end
end
def v1_order_params
params.require(:v1_order).permit(:state, :vat, :order_date, :user_id,
:item_lines_attributes => [:quantity, :net_price])
end
order_controller_spec.rb
expect {
post :create,
:v1_order => {vat: 20, user_id: 1,state: 0,
:item_lines_attributes => [quantity: 2, net_price: 3]},
token: #user.api_key.token
}.to change(V1::Order, :count).by(1)
**below should also be valid, but no go **
post :create,
v1_order: FactoryGirl.attributes_for(:v1_order,
item_lines_attributes: FactoryGirl.attributes_for(:v1_item_line)),
Factories
factory :v1_order, :class => 'V1::Order' do
state 0
vat 20
user_id 1
end
factory :v1_item_line, :class => 'V1::ItemLine' do
quantity 1
net_price "9.99"
end
keep getting this:
Failure/Error: post :create,
ActiveModel::ForbiddenAttributesError:
ActiveModel::ForbiddenAttributesError
Define def v1_order_params inside the private function.
Private
def v1_order_params
....
end
End
In your controller you can try to change:
#v1_order.item_lines.build(params[:v1_order][:item_lines_attributes])
To:
#v1_order.item_lines.build(v1_item_lines_params)
Related
Testing an Rails Controller (for an API) in RSpec for a nested resource (orders has_many order_actions):
it "returns status OK" do
order_action = create(:order_action)
order_action.update_attribute(:order_id, #order.id)
put :update, {:id => order_action.id, :order_id => #order.id, :order_action => valid_attributes}.merge(valid_session)
expect(response.code).to eql("200")
end
(valid_attributes is just build(:order_action).attributes. valid_session is {:user_email => #user.email, :user_token => #user.authentication_token, :format => 'json'}.)
I get an annoying error missing required keys: [:order_id]. Annoying because it is, as you can see, explicitly defined above. Twice. Here's the error text:
ActionController::UrlGenerationError:
No route matches {:action=>"show", :controller=>"api/v1/order_actions", :format=>nil, :id=>#<OrderAction id: 19, order_id: 3, created_at: "2015-03-13 15:52:13", updated_at: "2015-03-13 15:52:13">, :order_id=>nil} missing required keys: [:order_id]
And the relevant failed test report
rspec ./spec/controllers/order_actions_controller_spec.rb:151 # Api::V1::OrderActionsController PUT #update with valid params returns status OK
Notice two errors: :order_id and :format are nil.
Now, things get even weirder if you run the exact same test alone:
$ rspec spec/controllers/order_actions_controller_spec.rb:151
Run options: include {:locations=>{"./spec/controllers/order_actions_controller_spec.rb"=>[151]}}
.
Finished in 0.16575 seconds (files took 3.62 seconds to load)
1 example, 0 failures
The test passes! I'm losing my mind. Help?
Additional Info
#order and #user creation at the top of the order_actions_controller_spec.rb:
before(:each) do
#user = create(:user)
#order = create(:order)
end
Order and User factories:
a_user = User.new({:email => "#{SecureRandom.hex(3)}#rakuten.com", :password => "!abc123%##", :password_confirmation => "!abc123%##"})
FactoryGirl.define do
factory :order do
user a_user
class_code "MyText"
class_name "MyString"
complete false
end
end
and
FactoryGirl.define do
factory :user do
email "#{SecureRandom.hex(3)}#email.com"
password "!abc123%##"
password_confirmation { "!abc123%##" }
end
end
I am new to ruby on rails and I have been reading the Agile web development with rails book.
I am working on Iteration B1: Validating, and I am really confused by the symbol :product while I am working on the test. The question is about the ":product => #update"
I really have no idea what does this mean and where the :product symbol come from. I know it is a hash, but which table does it hash to? what exactly does it do here? The code is as following. Thank you in advance.
require 'test_helper'
class ProductsControllerTest < ActionController::TestCase
setup do
#product = products(:one)
#update = {
:title => 'Lorem Ipsum',
:description => 'Wibbles are fun!',
:image_url => 'lorem.jpg',
:price => 19.95
}
end
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:products)
end
test "should get new" do
get :new
assert_response :success
end
test "should create product" do
assert_difference('Product.count') do
***post :create, :product => #update***
end
assert_redirected_to product_path(assigns(:product))
end
# ...
test "should show product" do
get :show, :id => #product.to_param
assert_response :success
end
test "should get edit" do
get :edit, :id => #product.to_param
assert_response :success
end
test "should update product" do
put :update, :id => #product.to_param, :product => #update
assert_redirected_to product_path(assigns(:product))
end
# ...
test "should destroy product" do
assert_difference('Product.count', -1) do
delete :destroy, :id => #product.to_param
end
assert_redirected_to products_path
end
end
There is nothing magical about this construct. The #update is a variable name that references a hash. The hash is declared earlier in your test file.
#update = {
:title => 'Lorem Ipsum',
:description => 'Wibbles are fun!',
:image_url => 'lorem.jpg',
:price => 19.95
}
This hash contains new data that should be passed to the update action in the products controller. It's confusing because of the way the variable is named. A better name might help:
#product_attributes
The products#update action expects a hash containing updated data. That data is used to update the object.
The following line in your test...
post :update, :product => #update
Corresponds to this line you probably have in your products controller:
if #product.update_attributes(params[:product])
Notice params[:product]. It's basically saying "make a post request to products#update and pass it the #update hash as :product.
And that explains the symbol part. The :product symbol in your test is the name of the parameter that contains the product data, that the update action expects.
post :update, :product => #update
In theory you can call it whatever you want. But per convention it helps to call it the resource name.
I've got a problem with testing a create method of a controller. My test:
describe "POST #create" do
it "creates a new admin_user" do
expect{
post :create, :admin_user => FactoryGirl.attributes_for(:admin_user)
}.to change(Admin::User,:count).by(1)
end
end
And the failed spec I'm getting:
1) Admin::UsersController logged in POST #create creates a new admin_user
Failure/Error: expect{
count should have been changed by 1, but was changed by 0
# ./spec/controllers/admin_users_controller_spec.rb:75:in `block (4 levels) in <top (required)>'
Here's my controller:
def create
#admin_user = Admin::User.new(params[:admin_user])
if #admin_user.save
render nothing: true
end
end
and a factory:
require "factory_girl"
require 'ffaker'
FactoryGirl.define do
factory :admin_user, class: "Admin::User" do |f|
f.name {Faker::Name.first_name}
f.email {Faker::Internet.email}
f.password "aaa"
end
end
and my model:
class Admin::User < ActiveRecord::Base
has_secure_password
validates :email, :uniqueness => true, :presence => true
validates_presence_of :password, :on => :create
end
I have no idea what might be wrong. I've searched the whole internet for it but didn't find the answer. Adding user from the rails console works just fine. Any help would be highly appreciated. Thanks in advance.
This line:
post :create, FactoryGirl.attributes_for(:admin_user)
Should be this:
post :create, :admin_user => FactoryGirl.attributes_for(:admin_user)
As an exercise, print out the params (p params or puts params.inspect) in your create action in your controller and you'll see the difference.
EDIT You should try 2 things:
Still print out the param to see if they make sense to you.
The problem might be that your params just aren't valid. Instead of using save, try using save! which will throw an error if any of your validations are wrong, and you'll see the error in your test output. You should handle the situation if the save fails anyways.
I'm working through Hartl's book and I'm up to chapter 8. I've written some tests that I believe should be passing. I've quadruple checked my code against what's in the book, and double checked it against what's in the book's github repo, but I'm stumped. I'm getting the following errors from RSpec:
Failures:
1) UsersController POST 'create' should redirect to the user "show" page
Failure/Error: response.should redirect_to(user_path(assigns(:user)))
ActionController::RoutingError:
No route matches {:action=>"show", :controller=>"users", :id=>#<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil, encrypted_password: nil, salt: nil>}
# ./spec/controllers/users_controller_spec.rb:93:in `block (3 levels) in <top (required)>'
2) UsersController POST 'create' should have a welcome message
Failure/Error: flash[:success].should =~ /welcome to the sample app/i
expected: /welcome to the sample app/i
got: nil (using =~)
# ./spec/controllers/users_controller_spec.rb:98:in `block (3 levels) in <top (required)>'
Finished in 0.83875 seconds
46 examples, 2 failures
Like I said, I've checked the code again and again. Restarted spork, restarted rails server, ran without spork. I've checked it against the code in the book and in the github repo. I've even copy/pasted the spec and controller code in the github repo, but all to no avail.
I'm stumped. It's late and I need to crash. :)
Hopefully one of you guys can see something I'm not. Here's what I've got so far...
users_controller_spec.rb
require 'spec_helper'
describe UsersController do
render_views
# ...
describe "POST 'create'" do
# ...
describe 'success' do
before(:each) do
#attr = { :name => 'New User', :email => 'some-email#gmail.com', :password => 'foobar', :password_confirmation => 'foobar' }
end
it 'should create a new user' do
lambda do
post :create, :user => #attr
end.should change(User, :count).by(1)
end
end
it 'should redirect to the user "show" page' do
post :create, :user => #attr
response.should redirect_to(user_path(assigns(:user)))
end
it 'should have a welcome message' do
post :create, :user => #attr
flash[:success].should =~ /welcome to the sample app/i
end
end
end
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
#title = 'Sign up'
end
def show
#user = User.find params[:id]
#title = #user.name
end
def create
#user = User.new(params[:user])
if #user.save
flash[:success] = 'Welcome to the Sample App!'
redirect_to #user
else
#title = 'Sign up'
render 'new'
end
end
end
user.rb
class User < ActiveRecord::Base
# Virtual properties (don't exist in db)
attr_accessor :password
# Accessible properties
attr_accessible :name, :email, :password, :password_confirmation
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
before_save :encrypt_password
# Return true if the user's password matches the submitted password
def has_password?(submitted_password)
# Compare encrypted_password with the encrypted version of submitted_password
encrypted_password == encrypt(submitted_password)
end
# Static/Class methods
def self.authenticate(email, submitted_password)
user = find_by_email email
return nil if user.nil?
return user if user.has_password? submitted_password
end
# Private functionality.
# Anything after the 'private' pragma will be inaccessable from outside the class
private
def encrypt_password
self.salt = make_salt if new_record? # Using ActiveRecord goodness to make sure this only gets created once.
self.encrypted_password = encrypt(password)
end
def encrypt(string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
end
routes.rb
SampleApp::Application.routes.draw do
#get '/users/new'
resources :users
match '/signup' => 'users#new'
match '/about' => 'pages#about'
match '/contact' => 'pages#contact'
match '/help' => 'pages#help'
root :to => 'pages#home'
end
Thanks in advance. If you guys really want to dig through or if I've missed something in my post, here's my code.
I'm very new to rails, and absolutely love it so far. Any help will be greatly appreciated.
Look closely at your users_controller_spec "success" spec: when will it create #attr? Before each test, or just before the "should create a new user" test? You use it in all the "POST 'create'" tests...
Once you make that non-spec-specific your tests will pass.
(By the way, having the code up in git is handy, but only if the code you're posting is actually checked in, otherwise... not as much ;)
your
it "should redirect to the user show page"
and
it "should have a welcome message"
is outside of the
describe "success" do
loop
I've got two models:
class Solution < ActiveRecord::Base
belongs_to :owner, :class_name => "User", :foreign_key => :user_id
end
class User < ActiveRecord::Base
has_many :solutions
end
and I nest solutions within users like this:
ActionController::Routing::Routes.draw do |map|
map.resources :users, :has_many => :solutions
end
and finally here's the action I"m trying to spec:
class SolutionsController < ApplicationController
before_filter :load_user
def show
if(#user)
#solution = #user.solutions.find(params[:id])
else
#solution = Solution.find(params[:id])
end
end
private
def load_user
#user = User.find(params[:user_id]) unless params[:user_id].nil?
end
end
My question is, how the heck do I Spec #user.solutions.find(params[:id])?
Here's my current spec:
describe SolutionsController do
before(:each) do
#user = Factory.create(:user)
#solution = Factory.create(:solution)
end
describe "GET Show," do
before(:each) do
Solution.stub!(:find).with(#solution.id.to_s).and_return(#solution)
User.stub!(:find).with(#user.id.to_s).and_return(#user)
end
context "when looking at a solution through a user's profile" do
it "should find the specified solution" do
Solution.should_receive(:find).with(#solution.id.to_s).and_return(#solution)
get :show, :user_id => #user.id, :id => #solution.id
end
end
end
But that gets me the following error:
1)Spec::Mocks::MockExpectationError in 'SolutionsController GET Show, when looking at a solution through a user's profile should find the specified solution'
<Solution(id: integer, title: string, created_at: datetime, updated_at: datetime, software_file_name: string, software_content_type: string, software_file_size: string, language: string, price: string, software_updated_at: datetime, description: text, user_id: integer) (class)> received :find with unexpected arguments
expected: ("6")
got: ("6", {:group=>nil, :having=>nil, :limit=>nil, :offset=>nil, :joins=>nil, :include=>nil, :select=>nil, :readonly=>nil, :conditions=>"\"solutions\".user_id = 34"})
Can anybody help me with how I can stub #user.solutions.new(params[:id])?
Looks like I found my own answer, but I'm going to post it up here since I can't seem to find a whole lot about this on the net.
RSpec has a method called stub_chain: http://apidock.com/rspec/Spec/Mocks/Methods/stub_chain
which makes it easy to stub a method like:
#solution = #user.solutions.find(params[:id])
by doing this:
#user.stub_chain(:solutions, :find).with(#solution.id.to_s).and_return(#solution)
So then I can write an RSpec test like this:
it "should find the specified solution" do
#user.solutions.should_receive(:find).with(#solution.id.to_s).and_return(#solution)
get :show, :user_id => #user.id, :id => #solution.id
end
And my spec passes. However, I'm still learning here, so if anybody thinks my solution here is no good, please feel free to comment this and I try to get it totally right.
Joe
With the new RSpec syntax, you stub a chain like so
allow(#user).to receive_message_chain(:solutions, :find)
# or
allow_any_instance_of(User).to receive_message_chain(:solutions, :find)