16

次のように、コントローラーのテストで FactoryGirl.attributes_for を使用したいと考えています。

it "raise error creating a new PremiseGroup for this user" do
  expect {
    post :create, {:premise_group => FactoryGirl.attributes_for(:premise_group)}
  }.to raise_error(CanCan::AccessDenied)
end

...しかし、 #attributes_for が :user_id 属性を省略しているため、これは機能しません。#createとの違いは次の#attributes_forとおりです。

>> FactoryGirl.create(:premise_group)
=> #<PremiseGroup id: 3, name: "PremiseGroup_4", user_id: 6, is_visible: false, is_open: false)
>> FactoryGirl.attributes_for(:premise_group)
=> {:name=>"PremiseGroup_5", :is_visible=>false, :is_open=>false}

:user_id が にないことに注意してください#attributes_for。これは予想される動作ですか?

FWIW、私の工場ファイルには:premise_groupと の定義が含まれています:user:

FactoryGirl.define do
  ...
  factory :premise_group do
    sequence(:name) {|n| "PremiseGroup_#{n}"}
    user
    is_visible false
    is_open false
  end
  factory :user do
    ...
  end
end
4

5 に答える 5

26

簡潔な答え:

設計上、FactoryGirlattribues_forはデータベース トランザクションをトリガーするものを意図的に省略しているため、テストは高速に実行されます。ただしbuild_attributes、時間を割いても構わないと思っている場合は、すべての属性をモデル化するメソッド (以下) を作成できます。

元の答え

FactoryGirl のドキュメント (この wiki ページなど) を深く掘り下げると、関連付けを無視する言及が見つかりますattributes_for。以下の更新を参照してください。FactoryGirl.build(...).attributes回避策として、ストリップidcreated_at、およびの周りにヘルパー メソッドをラップしましたupdated_at

def build_attributes(*args)
  FactoryGirl.build(*args).attributes.delete_if do |k, v| 
    ["id", "created_at", "updated_at"].member?(k)
  end
end

だから今:

>> build_attributes(:premise_group)
=> {"name"=>"PremiseGroup_21", "user_id"=>29, "is_visible"=>false, "is_open"=>false}

...まさに期待通りです。

アップデート

FactoryGirl の作成者からのコメントを吸収して、attributes_for関連付けを無視する理由を理解しました。関連付けを参照すると、場合によってはテストが大幅に遅くなる可能性があるデータベースへの呼び出しが生成されます。ただし、関連付けが必要な場合は、build_attributes上記のアプローチが機能するはずです。

于 2012-04-24T08:26:44.540 に答える
1

これは、希望する結果にもよりますが、fearless_fool の回答よりもわずかに改善されていると思います。

例で説明するのが最も簡単です。モデルに緯度と経度の属性があるとします。フォームには、緯度と経度のフィールドはありませんが、緯度、緯度、緯度、緯度などです。これらは、後で 10 進数の緯度経度形式に変換できます。

あなたの工場が次のようになっているとしましょう:

factory :something
  lat_d 12
  lat_m 32
  ..
  long_d 23
  long_m 23.2
end

大胆不敵なものbuild_attributesが戻ってき{ lat: nil, long: nil}ます。build_attributes以下は戻りますが{ lat_d: 12, lat_m: 32..., lat: nil...}

def build_attributes
  ba = FactoryGirl.build(*args).attributes.delete_if do |k, v| 
    ["id", "created_at", "updated_at"].member?(k)
  end
  af = FactoryGirl.attributes_for(*args)
  ba.symbolize_keys.merge(af)
end
于 2012-12-21T05:40:01.160 に答える
0

ウェブを掘り下げた後、特にこのGithubの問題を掘り下げた後、私はあなたに提示します:

Rails 5+ の最も基本的な機能のクリーン バージョン

これにより、関連付けが作成 され、それらの(およびif ) が属性に:belongs_to追加されます。また、コントローラーに限定された独自のモジュールの代わりにコードを介して含まれています。idtype:polymorphicFactoryBot::Syntax::Methods

仕様/サポート/factory_bot_macros.rb

module FactoryBot::Syntax::Methods
  def nested_attributes_for(*args)
    attributes = attributes_for(*args)
    klass = args.first.to_s.camelize.constantize

    klass.reflect_on_all_associations(:belongs_to).each do |r|
      association = FactoryBot.create(r.class_name.underscore)
      attributes["#{r.name}_id"] = association.id
      attributes["#{r.name}_type"] = association.class.name if r.options[:polymorphic]
    end

    attributes
  end
end

これは、githubの問題に関するjamesst20の適応バージョンです-彼への称賛

于 2019-07-09T07:42:32.027 に答える