Reputation: 137
I am using acts_as_audited. In the controller on the destroy action I pass the audit comment value. All this works well but when I try to test the destroy action I get:
PurchasesController DELETE /destroy deletes the correct Purchase
Failure/Error: delete :destroy, id: i
NoMethodError:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occured while evaluating nil.[]
# ./app/controllers/purchases_controller.rb:79:in `destroy'
# ./spec/controllers/purchases_controller_spec.rb:156:in `block (3 levels) in <top (required)>'
Line #79 reads: @purchase.audit_comment = params[:purchase][:audit_comment]
Heres my code:
PurchasesController
def destroy
@purchase = Purchase.find(params[:id])
@purchase.audit_comment = params[:purchase][:audit_comment]
respond_to do |format|
if @purchase.destroy
format.html { redirect_to(purchases_url, notice: "Successfully destroyed purchase.") }
format.xml { render :xml => @purchase, :status => :deleted, :location => @purchase }
else
flash[:alert] = "#{@purchase.po_number} could not be destroyed"
render 'show'
end
end
end
Purchases show.html.erb EDITED
<%= form_for @purchase, method: :delete do |builder| %>
<% if @purchase.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@purchase.errors.count, "error") %> prohibited this purchase from being saved:</h2>
<ul>
<% @purchase.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% title "Purchase" %>
<div id="delete_button">
<span><strong>PO Number: <%= @purchase.po_number %></strong></span>
<%= render "audit_comment", f: builder %>
<%= builder.submit "Destroy Purchase"%>
</div>
<% end %>
<p>
<%= link_to "Edit", edit_purchase_path(@purchase) %> |
<%= link_to "View All", purchases_path %>
</p>
_audit_comment.html.erb - Purchases
<div id = "audit">
<%= f.label :audit_comment %>:<br />
<%= f.text_area :audit_comment, :size => "60x5" %>
</div>
purchase_controller_spec.rb
require 'spec_helper'
require 'ruby-debug'
describe PurchasesController do
login_user
render_views
before(:each) do
@purchase = Factory(:purchase)
end
describe "DELETE /destroy" do
before(:each) do
@ability.can :destroy, Purchase
end
it "deletes the correct Purchase" do
i = @purchase.id
c = Purchase.count
pl = Purchase.find(i).purchase_line_items
cpl = pl.count
delete :destroy, id: i
Purchase.count.should == c-1
pl.count.should == cpl-1
response.should redirect_to(purchases_path)
flash[:notice].should == "Successfully destroyed purchase."
end
it "redirects to the index page with an alert when a delete fails" do
i = @purchase.id
c = Purchase.count
pl = Purchase.find(i).purchase_line_items
cpl = pl.count
Purchase.any_instance.stubs(:valid?).returns(:false)
delete :destroy, id: i
Purchase.count.should_not == c-1
pl.count.should_not == cpl-1
response.should render_template('show')
flash[:alert].should == "#{@purchase.po_number} could not be destroyed"
end
end
end
Any help is appreciated. Thanks!
Upvotes: 1
Views: 3220
Reputation: 6515
It's telling you that at line 79, params[:purchase]
is nil.
The reason it's nil is that button_to
generates its own form tag. Thus, you now have a <form>
within a <form>
, and your audit comment field is not being submitted. Instead of button_to
, you should use builder.submit
. You will also need to set the :method
option in your call to form_for
to make it a DELETE
request.
Update after edit to question
The HTML looks OK now, but I see a problem with the spec. I think you're forgetting to pass the audit comment in to your HTTP params. It's there in the HTML, but your spec bypasses the form, because it tests the controller in isolation. (An integration test would use the actual form. Controller tests don't.) Therefore, you'll have to manually add to the request any form params the controller expects. For example:
delete :destroy, :id => 1, :purchase => {:audit_comment => 'Test comment'}
Upvotes: 2