Advertisement

More Related Content

Advertisement

Recently uploaded(20)

Fabrication

  1. Fabrication!
  2. 研究社 新英和中辞典 fabrication 音節 fab ・ ri ・ ca ・ tion 発音記号 / f`æbrɪkéɪʃən / 【名詞】 1 【不可算名詞】 製作 ; 作り 上げ , でっちあげ ; 偽造 . 2 【可算名詞】 作り ごと,うそ .
  3. 機械工学英和和英辞典 [fuel] fabrication 成型加工
  4. Fabrication is a simple, powerful object generation library. Fabrication はシンプルで強力な、 オブジェクト生成ライブラリです。 公式サイト( http://fabricationgem.org/ )より
  5. オブジェクト生成? ↓ これ MyObject.new MyObject.create
  6. それだけ? 何が嬉しいの?
  7. 例: もしもこんな クラスが あったら……
  8. class Book validates :title, presence: true validates :author, presence: true validates :price, presence: true validates :stock, presence: true end 本
  9. 4つの必須属性
  10. テスト: タイトルが空なら invalid になること
  11. book = Book.new(title: '', author: 'foo', publisher: 'bar', price: 100) book.should be_invalid 書いてみる
  12. book = Book.new(title: '', # < 空です author: 'foo', # < foo です publisher: 'bar', # < bar です price: 100) # < 100 です book.should be_invalid # < それは不正です コードから伝わる情報
  13. book = Book.new(title: '', # < お前らうるさい author: 'foo', # < えー publisher: 'bar', # < えー price: 100) # < えー book.should be_invalid # < それは不正です ノイズ。
  14. Fabrication!
  15. まず準備として
  16. Fabricator(:book) do title 'MyTitle' author 'MyAuthor' publisher 'MyPublisher' price 1 end
  17. 無難なデフォルト値を 定義しておく
  18. book = Fabricate.build(:book, title: '') book.should be_invalid 書いてみるⅡ
  19. book = Fabricate.build(:book, title: '') # < タイトルが空です book.should be_invalid # < それは不正です author < …… publisher < …… price < …… コードから伝わる情報Ⅱ
  20. 明確
  21. Fabrication!
  22. http://fabricationgem.org/
  23. ActiveRecord objects
  24. Mongoid Documents
  25. Sequel Models
  26. Installation - インストール -
  27. gem install fabrication
  28. Gemfile: # 基本的にテストで使うが、 # rails console でも使えると便利 group :development, :test do gem 'fabrication' end > bundle install
  29. Defining Fabricators - Fabricator の定義 -
  30. あるクラスに どういうデフォルト値を 与えるか?
  31. 例えばこんなクラス # name と job を持つ Person クラス Person.new( name: 'Taro', job: 'Programmer')
  32. # spec/fabricators/person_fabricator.rb Fabricator(:person) do name 'Taro' job 'Programmer' end :personというFabricatorの定義
  33. 実際に生成する
  34. 3種類の生成メソッド # new して save (create) したものを返す Fabricate (:person) # new だけして save せずに返す Fabricate. build (:person) # 属性の Hash を返す Fabricate. attributes_for (:person)
  35. person = Fabricate(:person, name: &quot;Hanako&quot; ) person.name # => “Hanako” person.job # => “Programmer” 属性の値を明示する
  36. # ( 引数の Hash の方が優先 ) Fabricate(:person, :name => &quot;Ichiro&quot;) do job &quot;Baseballer&quot; end ブロックも渡せる
  37. 基本的な使い方 以上。
  38. もっと詳しく Fabricator 定義
  39. # spec/fabricators/person_fabricator.rb Fabricator(:person) do name 'Taro' job 'Programmer' end 再掲
  40. Fabricator(:person) do # テストデータ生成ライブラリを使う name { Faker::Name.name } # ランダムな値を使う job { %w( Singer Desinger Manager).sample } end ブロックも使える
  41. Fabricator(:person) do name { %w(alice bob carol).sample } # 名前からメールアドレスを決める email { |person| &quot;#{ person.name }@example.com&quot; } end 属性は上から順に決まっていく
  42. # 呼ぶごとに 0 から増えてい く Fabricate. sequence # シーケンスに名前をつけて区別する Fabricate.sequence( :name ) # 0 じゃなく 99 から始める Fabricate.sequence(:number, 99 ) # ブロックも渡せる Fabricate.sequence(:name) { |i| &quot;Name #{i}&quot; } Sequence
  43. Fabricator(:person) do ssn { sequence(:ssn, 111111111) } email { sequence(:email) { |i| &quot;user#{i}@example.com&quot; } } end ユニーク制約にはSequence
  44. class Person belongs_to :vehicle # 乗り物 validates :vehicle_id, presence: true # … は必須 end # Vehicle は↓これでいいとして、 class Vehicle validates :name, presence: true end Fabricator(:vehicle) do name 'name1' end 例:Personは必ずVehicleを持つ
  45. # こう書けるが、 Fabricator(:person) do vehicle { Fabricate( :vehicle ) } end # これでも同じ意味になる Fabricator(:person) do vehicle end ただ属性名だけを書く
  46. 例:属性名とモデル名が違う場合 class Person belongs_to :ride, class_name: 'Vehicle' validates :vehicle_id, presence: true end # person.vehicle ではなく person.ride # で Vehicle オブジェクトにアクセス
  47. :fabricatorオプションを渡す # こう書けるが Fabricator(:person) do ride { Fabricate( :vehicle ) } end # これでも同じ意味になる Fabricator(:person) do ride( fabricator: :vehicle ) end
  48. 継承 Fabricator(:person) do name 'name1' age 20 end # :person をベースに :child を定義する Fabricator(:child, from: :person ) do age 10 end child = Fabricate(:child) child.name # => 'name1' child.age # => 10
  49. 対コンストラクタ # x と y が無いと new できない class Location def initialize(x, y) @x, @y = x, y end end Fabricator(:location) do on_init { init_with (30.284167, -81.396111) } end
  50. Callback Fabricator(:person) do # Fabricate.build 後に geolocate! する after_build { |person| place.geolocate! } # Fabricate(:person) 後に Restaurant を作る after_create { |person| Fabricate(:restaurant, place: person.place) } end
  51. Configuration - 設定 -
  52. ここに Fabricator 定義を置けば 勝手に読まれる spec/fabricators/**/*fabricator.rb test/fabricators/**/*fabricator.rb
  53. Fabrication.configure do |config| # fabricator 定義の置き場所 fabricator_dir = &quot;data/fabricators&quot; # 複数ある場合は配列でもよし # fabricator_dir = [&quot;data/fabricators&quot;, # &quot;spec/fabricators&quot;] end
  54. Fabrication!
  55. 使用上の注意 (リアル話)
  56. Fabricator(:item) do seller( fabricator: :shop ) main_category( fabricator: :category ) sub_categories( fabricator: :category, count: 3 ) users( fabricator: :user, count: 3 ) etc... etc... end 連鎖
  57. テスト時間 30 分 -> 2 時間
  58. これをプロジェクト初期に明示、周知
  59. 根本的に、 テストの速度は大事 。 教訓
  60. ちなみに、こうして解決しました Fabricator(:item) do main_category do Category.first || Fabricate(:category) end end by @kengos
  61. describe User do it 'full_name は苗字+名前になる ' do user = Fabricate(:user) user.full_name.should == 'Yamada Taro' end end テストの可読性
  62. テスト対象の値は明示する。 it 'full_name は苗字+名前になる ' do user = Fabricate(:user, first_name: 'Taro', last_name: 'Yamada' ) user.full_name.should == 'Yamada Taro' end
  63. 合わせて読みたいサイト2つ
  64. Test Context Arrangement Recipebook @moro, RubyKaigi2011 http://www. slideshare .net/moro/test-context-arrangement-recipebook 使い分けの研究
  65. resource (生成・変更される)
  66. event (関連)
  67. データ生成方法3種類
  68. fixture ( fixtures/foo.yml
  69. fixture replacement ( fabricator )
  70. before/setup ( Foo.new
  71. どれに何を使うべきか
  72. Fabrication の目的 ・テストを書きやすく ・テストを読みやすく
  73. RSpec の入門とその一歩先へ http://d.hatena.ne.jp/t-wada/20100228/p1 t-wadaの日記 RSpecも知っとこう
  74. ありがとう ございました

Editor's Notes

  1. “Fabrication”の意味は?-&gt;
  2. ネガティブな言葉ばっかり…
  3. これだ! 原料を型に流しこんで同じ物をたくさん作る!
  4. 公式サイトの説明文です。
  5. 例を挙げてみます
  6. 本。 注目すべきは… -&gt;
  7. presence。無いとダメ。空白許されない。 つまり、4つ値を設定しないと保存できない。
  8. こういうテストを書きたい。 書いてみましょう -&gt;
  9. こうまぁこう書くかなー titleはだけカラにして、 他はテキトーな値を入れて。
  10. コードから伝わる情報を そのまま読み取ってみると、こうなる。
  11. 余計な情報が多くて邪魔。 関心があるのはtitleだけなのに。 書くのも面倒。 ”テキトーな値”を考えるのも面倒。 そこで -&gt;
  12. Fabricationですよ
  13. Fabricationを使うとどうなるか?説明します。 まず準備が要ります。
  14. こんなコードを書いておきます。 bookのFabricator。
  15. MyTitle, MyAuthor。 テキトーだけどvalidationには引っかからない。 そんな値を予め用意しておく。
  16. Fabricatorを利用してbookを生成する。
  17. authorやらpublisherやらは出しゃばってこない。 titleだけについて書かれている。
  18. これが Fabrication 。 OK ? -&gt;
  19. 公式サイトの記述を追いながら、 その機能を説明していきます。
  20. Plain old Ruby objectsというのはつまり、 普通に定義した クラス 。 Fabricatorから使われることとか想定していないし、 フレームワークのクラスから派生したりしてない。 他3つはORマッパー。 ActiveRecordはRailsの。 自分はActiveRecordを生成するのにしか 使ったことがないので、 基本それを想定して説明していく。
  21. まずはインストール方法。
  22. コマンドラインからこう打ち込む
  23. railsなら、Gemfileにこう書く。 そしてbundle installとコマンドラインから打つ。 基本的にテストで使うものだが、 開発中にちょっとrails console立ち上げて 生成してみて動きを見る、みたいなことも よくやりたくなるので、 それができるように:developmentにも 入れておくと便利。推奨。 次に -&gt;
  24. これはつまり、デフォルト値の宣言。
  25. 名前と職業を持つPersonクラス。 こういうクラスがあるとすると… -&gt;
  26. :personというFabricatorをこうやって定義する。 これをspec/fabricators/person_fabricator.rbという ファイルに書いておく。 これで準備が整った。 そして -&gt;
  27. 3種類の生成の仕方がある。 1.オブジェクトを作ってDBに保存した上で返す。 2.DBには保存しない。   あえてvalidationに引っかかる奴を作るとか。   保存しないなら当然高速でもある。 3.Hashで。   これは元々のクラスのnewとかに渡せる。 与える引数の形式は、3つ全部同じ。 それをこれから説明します。 -&gt;
  28. 明示的にnameにHanakoを指定した。 nameは指定したHanako、 指定されてないjobは Fabricatorで指定されたデフォルトが使われる
  29. 先のFabricator定義と同じような書式での書き方。 使ったことはない。 同じ値を引数とブロック両方で指定すると、 引数のほうが使われる。
  30. 以上。 これでデフォルトを定義しといて、 必要な部分だけを明示して生成するという、 基本の使い方はわかったと思う。
  31. あとはFabricatorの定義に 色々な機能が使えることを説明したい。
  32. ここでさっき定義した Fabricator をおさらい。
  33. デフォルトが固定値では困る場合、 ブロックを使う。 ブロックはオブジェクト生成のたびに実行され、 その戻り値が値になる。 Fakerはデータ生成のためのデータ集みたいなもの。 BillとかSteveとかそれっぽい名前を ランダムに返してくれる。 %wは文字列配列のためのRubyのリテラル記法。 sampleは配列のどれか1つを ランダムに返すメソッド。
  34. ブロックには生成途中のオブジェクトが渡される。 ので、メールアドレスに名前を含めたりできる。 まず名前がランダムに決まり、 次にその名前を使ってアドレスを決めている。 では次の機能 -&gt;
  35. sequenceという機能がある。 DBのシーケンスと同じ、1ずつ増える数値機能。 プロセスが終了するまでリセットされない。
  36. ユニーク制約。 同じ値を持つオブジェクトが 複数あってはいけない場合。 sequence が使える。 これは公式サイトの例だが、 ssn 、社会保障番号という国民背番号を生成する。 Fabricator の中でも外でも sequence は呼び出せる。 これは中なので直接呼び出している。 外からは先程のように、 Fabricate.sequence とする。
  37. ヴィークル。 乗り物オブジェクトはとりあえずこのように Fabricatorも定義しておくとして、 :personの定義。 -&gt;
  38. 今見てきた知識だと上のように書くことになる。 下のようにも書ける。
  39. ヴぃーくる。 PersonはVehicleに、ride、乗っている。 という定義。
  40. まぁ上のように書けるが、 下のようにも書ける。
  41. 継承という機能。 すでにある:personというFabricatorを 一部上書きする形で、 :childという新しいFabricator定義を作る。 :childもまたPersonクラスだが、 age、年齢のデフォルトが 20から10に変更されている。 nameは引き続き&apos;name1&apos;。
  42. x, y座標を要求するLocationオブジェクト。 これに対するFabricatorもちゃんと定義できる。 on_initはnewの直前に呼ばれるコールバック。 ここでinit_withすると、newへの引数が渡せる。 このあと各属性に対してname=という プロパティ代入、setterの呼び出しが続く。
  43. 先ほどのon_initもそうだが、 他に2つのcallbackがある。 after_buildはnewの直後、saveされるされないに 関わらず呼び出される処理を宣言できる。 after_createはFabricateでDBに保存されたあとで 呼び出される。buildでは呼ばれない。 先ほどのon_initとafter_buildを組み合わせれば、 Plain old Ruby objectもある程度生成できる。 自分ですでに書いてあるclassでも。 それとafter_createのポイントの1つは、 ここならidが取得できること。 ただしbuild-&gt;saveでは呼ばれない。
  44. 以上で Fabricator の定義の説明は打ち切り。 残った Configuration の説明。
  45. デフォルトではここにfabricatorを置く。
  46. そこじゃ困るって場合は、 こうして変更できるようになってる。 まぁそれだけ。
  47. 以上が公式サイトの説明。 これが Fabrication です。 あとはせっかくなので、 私見とかおせっかいとか。 もーちょっと。
  48. 実際に遭遇したトラブルから1点と、 自分の私見やおせっかいなどが少し続きます。
  49. こんなコードが書かれたことがあった。 1個の商品を生成すると、 それに関連するいろんなオブジェクトが 連鎖的に作られる。 履歴から読んでみたらなんと40個。 商品を生成してるテストはそりゃあ沢山あった。 その結果どうなったか -&gt;
  50. ……
  51. 私学びました。 rspecが「最も遅いテスト・ワースト10」を 表示できることも知りました。 方法は調べて。
  52. 他人のhackを勝手に自分のプレゼンに流用! ズルイ! 一見コワイ! しかし、実際デフォルト値に過ぎないので アクセスされないのが前提! なので上手くいく! ワザマエ!
  53. さらっと。 こう書いてしまうかもしれない。 Yamada Taroという値になる理由は? Fabricatorにこれがデフォルトとして 書かれているからなんですが。
  54. テストでデフォルト値を繰り返す価値はある。 これは重複ではない。
  55. おせっかい。 これで最後だから、もーちょっとだけ。
  56. RubyKaigi2011におけるmoroさんの発表。 slideshareに上がってる。 テストsetupの整頓方法とでも言うべきか。 個人研究、議論の叩き台だという断りはあり。
  57. どういう発表かというと、 データって3種類に分類できね? データ生成方法も3種類あるじゃん? (そのうち1つがFabricationのような  fixture replacement) どれを使うべきか? と、更にrspecの機能も活用することで、 よりテストを読みやすく、書きやすく。 Fabricatorをより適切に使いこなすために 役に立つかもしれない。
  58. 最後。
  59. そもそもテスト書いたことないよ! Fabrication以前にテスト自体チンプンカンプンさ! という人に、rspec入門記事として。 そうでない人でも、 テストフレームワークの機能を詳しく知ることで、 重複を省いたよりよいテストが書けるはず。 入門からさらに一歩、二歩、踏み込む記事として。 これを紹介して、ようやくこの発表はお終いです。
  60. 何か質問があればどうぞ。
Advertisement