0

First post on stackoverflow, apologies beforehand for anything noob-like.

I have been receiving an error on one of my functional tests and after many hours, I just can't seem to figure out how to pass it.

In short, an invoice_schedule which has_many invoice_milestones. Then, invoice_milestones has an attribute, estimate_percentage (an integer). My model models/invoice_schedule.rb has a validation which requires the sum of all estimate_percentages must equal 100.

The functional test for the invoice_schedule create action fails every time. With my current code below it has been erroring out instead. I can't see how it would error out with the params I'm passing.

I'll also note that estimate has_one :invoice_schedule

Any help or suggestions are appreciated :)

models/invoice_schedule.rb:

class InvoiceSchedule < ActiveRecord::Base belongs_to :estimate has_many :invoice_milestones, :dependent => :destroy accepts_nested_attributes_for :invoice_milestones, :allow_destroy => true validate :total_must_equal_one_hundred_percent def total_must_equal_one_hundred_percent if total_percent != 100 errors.add(:invoice_milestones, "Total must equal 100%") end end def total_percent invoice_milestones.to_a.sum { |item| item.estimate_percentage || 0 } end end 

models/invoice_milestone.rb:

class InvoiceMilestone < ActiveRecord::Base belongs_to :invoice_schedule belongs_to :invoice validates :estimate_percentage, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0, :less_than_or_equal_to => 100} end 

controllers/invoice_schedules_controller.rb

 def create @invoice_schedule = InvoiceSchedule.new(params[:invoice_schedule]) @estimate = Estimate.find_by_id(params[:estimate_id]) respond_to do |format| if @invoice_schedule.save format.html { redirect_to invoice_schedule_path(@invoice_schedule), :flash => {:notice => 'Invoice schedule was successfully created.', :status => 'success'} } format.json { render json: @invoice_schedule, status: :created, location: @invoice_schedule } else format.html { render action: "new" } format.json { render json: @invoice_schedule.errors, status: :unprocessable_entity } end end end 

test/functional/invoice_schedules_controller_test.rb

 setup do @account = accounts(:lorem) @estimate = estimates(:lorem_one) @user = users(:lorem_vendor) @request.host = "#{@account.subdomain}.myapp.local" session[:user_id] = @user.id @invoice_schedule = invoice_schedules(:lorem_one) @invoice_milestone = invoice_milestones(:lorem_one) @data_estimate = estimates(:lorem_two) @data_invoice_schedule = @invoice_schedule.attributes.merge({estimate_id: @data_estimate.id}) end test "should create invoice_schedule" do assert_difference('InvoiceSchedule.count') do post :create, :invoice_schedule => { estimate_id: @data_estimate, :invoice_milestone => {estimate_percentage: 100}} end assert_redirected_to estimate_invoice_schedule_path(@estimate, assigns(:invoice_schedule)) end 

config/routes.rb

require 'subdomain' Accountimize::Application.routes.draw do resources :invoices do member do get 'generateInvoiceFromMilestone' end end resources :users resources :sessions get "sign_up" => "accounts#new", :as => "sign_up" resources :accounts resources :clients do get :client_address, on: :member end resources :estimates do resources :invoice_schedules, :shallow => true end resources :line_items constraints(Subdomain) do match '/' => 'accounts#show' get "log_in" => "sessions#new", :as => "log_in" get "log_out" => "sessions#destroy", :as => "log_out" get "register" => "users#new", :as => "register" end root :to => 'site#index', :as => 'site' end 

The trace of the error I get:

InvoiceSchedulesControllerTest test_should_create_invoice_schedule ERROR No route matches {:invoice_schedule=>{:estimate_id=>"706507935", :invoice_milestones_attributes=>{"0"=>{:description=>"test", :estimate_percentage=>"100"}}}, :controller=>"invoice_schedules", :action=>"create"} STDERR: Exception `ActionController::RoutingError' at /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_dispatch/routing/route_set.rb:465:in `raise_routing_error' /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_dispatch/routing/route_set.rb:461:in `rescue in generate' /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_dispatch/routing/route_set.rb:453:in `generate' /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_dispatch/routing/route_set.rb:494:in `generate' /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_dispatch/routing/route_set.rb:490:in `generate_extras' /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_dispatch/routing/route_set.rb:486:in `extra_keys' /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_controller/test_case.rb:145:in `assign_parameters' /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_controller/test_case.rb:438:in `process' /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_controller/test_case.rb:49:in `process' /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_controller/test_case.rb:370:in `post' test/functional/invoice_schedules_controller_test.rb:35:in `block (2 levels) in <class:InvoiceSchedulesControllerTest>' /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/activesupport-3.1.1/lib/active_support/testing/assertions.rb:55:in `assert_difference' test/functional/invoice_schedules_controller_test.rb:30:in `block in <class:InvoiceSchedulesControllerTest>' /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/activesupport-3.1.1/lib/active_support/testing/setup_and_teardown.rb:35:in `block in run' /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/activesupport-3.1.1/lib/active_support/callbacks.rb:444:in `_run_setup_callbacks' /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/activesupport-3.1.1/lib/active_support/callbacks.rb:81:in `run_callbacks' /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/activesupport-3.1.1/lib/active_support/testing/setup_and_teardown.rb:34:in `run' 

I've tried quite a few other ways to accomplish adding an invoice_milestone to a newly created invoice_schedule in the functional test, however nothing seems to have worked for me. Why would I receive the above routing error if accepts_nested_attributes_for is added?

1 Answer 1

3

There are two parts to this. First you need to get the routing side right. Because you've nested invoice schedules inside estimates you need to supply an estimate_id at the top level (not inside params[:invoice_schedules]), i.e.

post :create, :estimate_id => @data_estimate.id, :invoice_schedule => {...} 

The expectation is then that you controller does something along the lines of

estimate = Estimate.find(params[:estimate_id]) @invoice_schedule = estimate.invoice_schedules.build params[:invoice_schedule] 

The second part is to do with nested attributes - you need to provide a params hash that matches what Rails expects. The first part of that is that the key in the hash should be :invoice_milestones_attributes. Since this is a has_many, the corresponding value needs to be an array of hashes, or a hash of hashes (the keys are ignored - this is largely present because of interaction between arrays and hashes in a rails' parameter passing), for example

post :create, :estimate_id => @data_estimate.id, :invoice_schedule => { :invoice_milestones_attributes => [ {:estimate_percentage => 100} ] } 
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks very much! However, my test is now failing I've used the following line for the test: post :create, :estimate_id => @data_estimate.id, :invoice_schedules => { :invoice_milestones_attributes => {0 => {estimate_percentage: 100}}} The test fails because of the total_must_equal_one_hundred_percent validation. Commenting it out makes the test pass. However, removing this validation and adding a validation to ensure an invoice_schedule has at least one invoice_milestone causes the test to fail, which suggests that the invoice_milestone attributes are not creating a new invoice_milestone.
You could stick a breakpoint in there to see what is actually happening, rather than trying to infer it
That helped trace it back. Thanks very much for the help!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.