読者です 読者をやめる 読者になる 読者になる

woshidan's blog

そんなことよりコードにダイブ。

FactoryGirlのアソシエーションの使い方を確認する

Ruby on Rails

テスト駆動開発についていまさらでもなんでも読んでいる方がいたので、
自分もFactoryGirlについていまさらでもなんでも確認する。

とにかく少しでも簡潔に速くテストが書けるようになればいいんだよ。
なお、動作確認用のテストはrspecではなくてminitestです。

 

参考にしたのはこちら。

factory_girl で最低限知っておきたい4つの使い方 - (゚∀゚)o彡 sasata299's blog  

FactoryGirl で多対多のアソシエーションのつくり方 - 彼女からは、おいちゃんと呼ばれています


FactoryGirlでアソシエーションの設定をする

  • 1対多
  • 1対1
  • 多対多

の場合があるけれど、1対多と多対多の場合について書く。

1対1は1対多と変わらないので……。

1対多の場合

associationの書き方は、factory名とclass名が一致するかどうかで異なる。
factory名とclass名が一致する場合は、factory名を書くだけでいい。
みたいに関連したモデル名を左に書いたりしなくていい。
factory名とclass名が異なる場合は、

という風にする。

テストを書いてみて、関連づけたAuthorモデルのオブジェクトもデータベースに保存されていることが確認。
データベースに保存されているのか、いないのかまだ少し不安になる。

多対多の場合

モデルが

のような場合。この場合、手順としては、

  • まず1個目のモデル(Category)のfactoryを普通に定義する。
  • 次に、2個目のモデル(Book)を定義するfactoryブロックの中でafter()を使う。
  • after(:create)に渡すブロックのなかでアソシエーション用のオブジェクトを作成して、2つのオブジェクトのインスタンスを引数に取らせる

という感じで、出来あがりは、こんな感じ。


なんだか最初に書いたとき不安になったので自分のために書いておくと、
ファイルは分かれていてもfactory名があっていたら大丈夫です。


おお、きちんとあるある。


ただ、この方法だと問題があって、

Bookオブジェクトを生成するたびにCategoryの方も必ず、その都度生成されてしまいます。
これは、Bookオブジェクトをいくつか生成して、その後関連づけされているCategoryのidで検索するテストを書くような場合、困る。

どうするか、というと、自分の場合,after(:create)のブロックの中を
みたいにユニークな属性(ここでは仮にnameとする)でfind_byしてみて、
見つからなかったらFactoryGirl.createするように書き換えてる。

find_or_created_by(Rails 4.0以降)メソッドを使ってもいい気がしたけど、
FactoryGirlを使わないと他の属性の設定が手動になってfactoryを書いた意味とは()となったので止めた。

ちなみに
sequence(:attribute) { |n| ... }
は:attributeで指定した属性に対してブロックの返り値を返す。
sequenceメソッドを読んだ回数 - 1が上のブロック中のnになる。


上だと、カテゴリの値を一種類しか用意できないじゃないか、という気がしたけど特に気のせいではありません。
部分的にcategoryの部分だけ書き換えたい、みたいな場合は次の記事のinheritanceとかtraitとかを使う。

 

2014/11/03追記

after(:create)のコールバックでやっている部分、lazy attribute blockで書けばいいですね。

lazy attribute blockというのは、これです。

このsequenceに渡しているようなブロック。

チュートリアルの影響でsequenceの使い方の一部としてしか認知していなかったんですが、

属性名のメソッドに対して、{}でブロックを与えると(do .. endだと×)ブロックの返り値を属性の値にすることができます。

基本的にFactoryGirlの属性名のメソッドには静的な値かfactoryしか渡せないようになっているのですが、lazy atttibute blockを使うと以下のように動的な値を渡すことができます。

ただ、こういう静的に前もって値が設定されていることを前提としてレコードを生成する場合は、

その前もって生成されているレコードをfixtureに書いておいたほうがいいかもしれません。

 

こんな感じに。

説明無く書いてますが、15.rbみたいにhas_manyのアソシエーションに配列でfactoryや{}を使って作成した配列を渡すと一度に複数の関連したレコードが作成できます。

ただ、こんな風にガチガチにデータベース上のレコードが関連しているテストを書くのであればfixtures使った方がいいのかもしれないです。