#ruby-on-rails-3.1 #rspec2 #factory-bot
#ruby-on-rails-3.1 #rspec2 #factory-бот
Вопрос:
Я пытаюсь написать спецификацию контроллера для создания покупки с позицией покупки. Покупка создается просто отлично со всеми атрибутами, которые я ей даю, но позиция строки покупки не создается. Вот мой код:
factories.rb
FactoryGirl.define do
factory :purchase do |f|
f.sequence(:po_number) { |n| "0000-00#{n}" }
f.sequence(:ship_to) { |n| "Test Corp#{n}"}
f.sequence(:ship_via) {|n| "Test Delivery#{n}" }
f.sequence(:terms) {|n| "My terms#{n}" }
f.sequence(:qa_requirements) {|n| "Requirment#{n}" }
f.sequence(:justification) {|n| "Justification#{n}" }
f.become_part_of_delivered_product true
f.immediately_delivered_to_stc_client false
f.remain_for_contract_at_stc_as_gov_prop false
f.consumed_in_job_requirements true
f.used_in_repair_of_gov_prop false
f.is_gov_prop_in_possession_of_stc false
f.sequence(:required) {|n| "2011-10-0#{n}" }
f.prd_number_id { |n| n.association(:prd_number).id }
# f.order_for_id { |o| o.association(:user) }
f.submitted_by_id { |s| s.association(:submitted_by).id }
# f.ordered_for_date { Time.now.to_s }
f.submitted_by_date { Time.now.to_s }
f.quality_system_classification_id { |q| q.association(:quality_system_classification).id }
f.after_create { |c| Factory(:purchase_line_item, purchase: c) }
end
factory :purchase_line_item do |f|
f.sequence(:item_description) {|n| "Desc#{n}" }
f.unit_price "100.00"
f.qty "40"
f.sequence(:charge_number) {|n| "000-00#{n}" }
end
end
PurchaseLineItem.rb
belongs_to :purchase
validates_presence_of :item_description,
:unit_price,
:qty,
:charge_number
Purchase.rb
belongs_to :authorized_signers
belongs_to :vendor
belongs_to :purchase_type
belongs_to :quality_system_classfication
belongs_to :order_by, :class_name => "User", :foreign_key => "order_by_id"
belongs_to :submitted_by, :class_name => "User", :foreign_key => "submitted_by_id"
belongs_to :approved_by, :class_name => "User", :foreign_key => "approved_by_id"
belongs_to :purchased_by, :class_name => "User", :foreign_key => "purchased_by_id"
has_many :purchase_line_items
has_many :audits
accepts_nested_attributes_for :purchase_line_items, :allow_destroy => true
validates_presence_of :required,
:ship_to,
:ship_via,
:terms,
:quality_system_classification_id,
:prd_number_id
Купить контроллер
load_and_authorize_resource
def new
@purchase = Purchase.new
1.times { @purchase.purchase_line_items.build }
end
def create
@purchase = Purchase.new(params[:purchase])
@purchase.without_auditing do
if @purchase.save
flash[:notice] = "Successfully created purchase."
redirect_to @purchase
else
render action: 'new'
end
end
end
Спецификация PurchaseController
require 'spec_helper'
describe PurchasesController do
login_user
describe "POST create" do
before(:each) do
@ability.can :create, Purchase
end
it "should pass the params to purchase" do
purchase = Factory(:purchase)
post :create, purchase: purchase.attributes.except("id")
assigns(:purchase).po_number.should == purchase.po_number
end
it "should pass the params to purchase_line_items" do
purchase = Factory(:purchase)
post :create, purchase: purchase.attributes, purchase_line_items_attributes: purchase.purchase_line_items.first.attributes
assigns(:purchase).purchase_line_items.first.unit_price.should == purchase.unit_price
end
end
end
Заранее спасибо
Ответ №1:
В Rails 4 эта общность для меня не работала:
post :create, purchase: { attributes: p, purchase_line_items_attributes: [pl] }
Ниже приведен обобщенный стиль, который, как я обнаружил, удовлетворит post :create …обновлено для Rails 4. Примечание: valid_session может работать не так, как я пытаюсь, но вы поняли СУХУЮ идею 😉
describe PurchasesController do
def valid_session
controller.stub!(:authorize).and_return(User)
end
describe 'POST :create' do
before do
purchase = FactoryGirl.build(:purchase).attributes
purchase_line_item = { purchase_line_items_attributes: { "#{rand(903814893)}" => FactoryGirl.build(:purchase_line_item).attributes } }
@valid_attributes = purchase.merge(purchase_line_item)
end
context "with valid params" do
before(:each) do
post :create, { purchase: @valid_attributes }, valid_session
end
it "assigns a newly created purchase as @purchase" do
assigns(:purchase).should be_a(Purchase)
end
it "saves a newly created purchase" do
assigns(:purchase).should be_persisted
end
it "redirects to the created purchase" do
response.should redirect_to(Purchase.last)
end
end
context "with invalid params" do
before do
Purchase.any_instance.stub(:save).and_return(false)
post :create, { purchase: @valid_attributes }, valid_session
end
it "assigns a newly created but unsaved purchase as @purchase" do
assigns(:purchase).should be_a_new(Purchase)
end
it "re-renders the :new template" do
response.should render_template("new")
end
it "returns http success" do
response.should be_success
end
end
end
end
В Rails 4, если вы настроили контроллер для использования:
params.require(:purchase).permit(...)
и удаление attr_accessible
из моделей, тогда вам не нужно беспокоиться об использовании attributes.except(...)
фильтра to.
Комментарии:
1. Спасибо за обновление! Спецификация, которую я опубликовал, была своего рода фиктивной спецификацией для меня, чтобы выяснить, как передавать параметры в фактический тест. Ваш ответ, безусловно, лучший способ протестировать контроллер.
2. @eric-wanchen Спасибо! Ваш
valid_session
шаблон — очень полезное и элегантное решение для тестирования контроллеров Rails 4, имеющих разрешения cancan.3. Это работает. Мы также можем вручную создать параметры, выполнив
parameters = ActionController::Parameters.new
, а затем выполнить все операции, включенные в API. Мы должны.require(:purchase).permit!
передать параметры как 2 отдельных объекта, например.def purchase_params params.require(:purchase).permit(...) end
а затем другие параметры. api.rubyonrails.org/classes/ActionController /…
Ответ №2:
Понял это. Я кое-что изменил, но проблема заключалась в синтаксисе. В спецификации PurchasesController я изменил код на:
describe PurchasesController do
login_user
render_views
before(:each) do
@purchase = Factory(:purchase)
end
describe "POST create" do
before(:each) do
@ability.can :create, Purchase
end
it "should pass the params to purchase" do
i = @purchase.id
p = @purchase.attributes.except('id', 'created_at', 'updated_at')
pl = @purchase.purchase_line_items.first.attributes.except('id', 'purchase_id', 'created_at', 'updated_at')
post :create, purchase: { attributes: p, purchase_line_items_attributes: [pl] }
assigns(:purchase).attributes.except('id', 'created_at', 'updated_at').should == p
assigns(:purchase).purchase_line_items.first.attributes.except('id', 'purchase_id', 'created_at', 'updated_at').should == pl
end
end
end
Синтаксис необходимо было изменить с:
post :create, purchase: purchase.attributes, purchase_line_items_attributes: purchase.purchase_line_items.first.attributes
Для:
post :create, purchase: { attributes: p, purchase_line_items_attributes: [pl] }
Теперь все работает!