Fabrication

4,568 views

Published on

Published in: Technology
0 Comments
12 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,568
On SlideShare
0
From Embeds
0
Number of Embeds
303
Actions
Shares
0
Downloads
26
Comments
0
Likes
12
Embeds 0
No embeds

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

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

    ×