Upgrade to Pro — share decks privately, control downloads, hide ads and more …

実践[email protected] #11

実践[email protected] #11

架空の実装に対して、自分が実践しているRspecのスタイルを説明しました。参加者とのディスカッションも巻末に載せています。

Hiroki Kishi

August 26, 2020
Tweet

More Decks by Hiroki Kishi

Other Decks in Technology

Transcript

  1. Controller Service Model DB 5 5 5 5 25 125

    625 合計775パタ⑲ン 全てを行うことは不可能だから、諦めている
  2. Controller Service Model DB 5 5 5 5 25 125

    625 Modelのテストと重複 ServicehModelのテストと重複
  3. たとえば class Foo def complex_method response = request(params, headers) object

    = ResponseHandler.new(response).do_something begin if object.new? AModel.create!(object.to_h) else AModel.update!(object.to_h) end rescue => e logger.error(e) end end end 例外が発生したら? 例外が発生したら? 分岐 例外が発生したら? 例外が発生したら? ここだけ握りつぶしてる
  4. describe Foo do describe '#do_something' do let(:foo) { Foo.new }

    subject { foo.complex_method } context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end end end end
  5. 1.セットアップフェ⑲ズ • subject, allow, before, テスト環境のデ⑲タ準備など。 • itやcontextで変わるものを変数化して、各テストフェ⑲ズで切り替える • くり返し使えるテストはshared_examples_for(または

    shared_context)で共通化しておく • hhhということをしてできる限り外部モジュ⑲ルへの依存性を排除する • このフェ⑲ズをしっかりやっておくことで、様々なバリエ⑲ションのテスト が少ないコ⑲ドで書ける!
  6. describe Foo do describe '#do_something' do let(:foo) { Foo.new }

    subject { foo.complex_method } context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end end end end class Foo def complex_method response = request(params, headers) object = ResponseHandler.new(response).do_something begin if object.new? AModel.create!(object.to_h) else AModel.update!(object.to_h) end rescue => e logger.error(e) end end end
  7. describe Foo do describe '#do_something' do let(:foo) { Foo.new }

    subject { foo.complex_method } context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end end end end class Foo def complex_method response = request(params, headers) object = ResponseHandler.new(response).do_something begin if object.new? AModel.create!(object.to_h) else AModel.update!(object.to_h) end rescue => e logger.error(e) end end end
  8. describe Foo do describe '#do_something' do let(:foo) { Foo.new }

    subject { foo.complex_method } context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end end end end class Foo def complex_method response = request(params, headers) object = ResponseHandler.new(response).do_something begin if object.new? AModel.create!(object.to_h) else AModel.update!(object.to_h) end rescue => e logger.error(e) end end end ブロック渡しがシンプルで
  9. describe Foo do describe '#do_something' do let(:foo) { Foo.new }

    subject { foo.complex_method } context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end end end end class Foo def complex_method response = request(params, headers) object = ResponseHandler.new(response).do_something begin if object.new? AModel.create!(object.to_h) else AModel.update!(object.to_h) end rescue => e logger.error(e) end end end doubleの宣言と一緒に メソッド定義できるのが
  10. describe Foo do describe '#do_something' do let(:foo) { Foo.new }

    subject { foo.complex_method } context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end end end end class Foo def complex_method response = request(params, headers) object = ResponseHandler.new(response).do_something begin if object.new? AModel.create!(object.to_h) else AModel.update!(object.to_h) end rescue => e logger.error(e) end end end doubleの宣言と一緒にメソッド定義できるのが
  11. describe Foo do describe '#do_something' do let(:foo) { Foo.new }

    subject { foo.complex_method } context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end end end end class Foo def complex_method response = request(params, headers) object = ResponseHandler.new(response).do_something begin if object.new? AModel.create!(object.to_h) else AModel.update!(object.to_h) end rescue => e logger.error(e) end end end
  12. おまけ: double, spy, partial double いずれも仮の振る舞いをするオブジェクト。 • double ◦ 振る舞いを定義(allow)していないメソッドを呼ぶとエラ⑲

    ◦ 同じオブジェクトに対して戻り値を定義する必要が無いメソッドをいくつも呼ぶ場合は、 いちいちallowするのが面倒 • spy ◦ 振る舞いを定義(allow)していないメソッドを呼ぶとnilが返る ◦ 今後変更されないであろう使い捨てのバッチ(Rake)を書くときは、 spyで済ませちゃうことも • partial double ◦ 「allow(User).to receive(:find) { ... }」と定義されたオブジェクト。 ◦ 実際のオブジェクトを元に、一部だけ振る舞いを定義し直しているのでpartial def spy(*args) double(*args).as_null_object end https://github.com/rspec/rspec-mocks/blob/main/lib/rspec/mocks/example_methods.rb#L120-L122
  13. describe Foo do describe '#do_something' do let(:foo) { Foo.new }

    subject { foo.complex_method } context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end end end end
  14. describe Foo do describe '#do_something' do let(:foo) { Foo.new }

    subject { foo.complex_method } context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end end end end class Foo def complex_method response = request(params, headers) object = ResponseHandler.new(response).do_something begin if object.new? AModel.create!(object.to_h) else AModel.update!(object.to_h) end rescue => e logger.error(e) end end end もともとの挙動を使いたいときに使えて
  15. describe Foo do describe '#do_something' do let(:foo) { Foo.new }

    subject { foo.complex_method } context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end end end end class Foo def complex_method response = request(params, headers) object = ResponseHandler.new(response).do_something begin if object.new? AModel.create!(object.to_h) else AModel.update!(object.to_h) end rescue => e logger.error(e) end end end .once .twice .exactly(n).time .exactly(n).times .at_least(:once) .at_least(:twice) .at_least(n).time .at_least(n).times .at_most(:once) .at_most(:twice) .at_most(n).time .at_most(n).times 覚えられない
  16. describe Foo do describe '#do_something' do let(:foo) { Foo.new }

    subject { foo.complex_method } context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end end end end class Foo def complex_method response = request(params, headers) object = ResponseHandler.new(response).do_something begin if object.new? AModel.create!(object.to_h) else AModel.update!(object.to_h) end rescue => e logger.error(e) end end end
  17. describe Foo do describe '#do_something' do let(:foo) { Foo.new }

    subject { foo.complex_method } context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end end end end class Foo def complex_method response = request(params, headers) object = ResponseHandler.new(response).do_something begin if object.new? AModel.create!(object.to_h) else AModel.update!(object.to_h) end rescue => e logger.error(e) end end end
  18. describe Foo do describe '#do_something' do let(:foo) { Foo.new }

    subject { foo.complex_method } context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end end end end class Foo def complex_method response = request(params, headers) object = ResponseHandler.new(response).do_something begin if object.new? AModel.create!(object.to_h) else AModel.update!(object.to_h) end rescue => e logger.error(e) end end end
  19. describe Foo do describe '#do_something' do let(:foo) { Foo.new }

    subject { foo.complex_method } context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end end end end class Foo def complex_method response = request(params, headers) object = ResponseHandler.new(response).do_something begin if object.new? AModel.create!(object.to_h) else AModel.update!(object.to_h) end rescue => e logger.error(e) end end end
  20. context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: {

    a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end バリエ⑲ションを増やす context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end context 'リクエストが成功して新しいレコ⑲ド作成に成功した場合' do let(:object) { double(:object, new?: true, to_h: { a: :b}) } let(:response_handler) { double(:response_handler, do_something: object) } before do allow(foo).to receive(:request) { { bar: 1} } allow(ResponseHandler).to receive(:new) { response_handler } allow(AModel).to receive(:create!).and_call_original allow(AModel).to receive(:update!).and_call_original end it 'レコ⑲ドが1件できている' do subject expect(AModel.count).to eq 1 expect(foo).to have_received(:request).with(...).once expect(ResponseHandler).to have_received(:new).with(bar: 1).once expect(response_handler).to have_received(:do_something).once expect(AModel).to have_received(:create!).with({ a: :b }).once expect(AModel).not_to have_received(:update!) end セットアップフェ⑲ズで赤字のところを可変にしておいて 、各context毎に異なる値を設定して様々な挙動を網羅し ていく • double(名前, メソッド名: リタ⑲ン値) • allow(オブジェクト).to receive(メソッド名) { リタ⑲ン値 } • allow(オブジェクト).to receive(メソッド名).and_raise(エラ⑲) 共通部分ごとごっそりコピ⑲して、必要なところだけ変え るのはやめよう • コピペで数千行の追加差分があるPRを見てそっ閉じしました
  21. fizzbazz.run_upto(15) fizz_count = 1 expect(fizzbazz).to have_received(:fizz).exactly(5).times do |n| expect(n).to eq

    fizz_count * 3 fizz_count += 1 end おまけ: 複数回呼ばれるメソッドの引数検証 各メソッドコ⑲ル時の引数を受け取るブロック
  22. おまけ: 他にも使えそうなメソッドたち rspec-mocks/lib/rspec/mocks/syntax.rb • receive_messages rspec-mocks-3.9.1/lib/rspec/mocks/example_methods.rb • double, spy •

    instance_double, instance_spy ◦ クラスを渡すと、そのクラスに実装されたインスタンスメソッドが呼べるdouble/spyが返る • object_double, object_spy ◦ オブジェクトを渡すと、そのオブジェクトに実装されたメソッドが呼べるdouble/spyが返る ◦ 動的に生やされたオブジェクトメソッドまで再現される! • class_double, class_spy ◦ クラスを渡すと、そのクラスに実装されたクラスメソッドが呼べるdouble/spyが返る • stub_const, hide_const ◦ constantsを変えられる。バッチサイズ10,000とかの処理をテスト用に10に書き換えればテストも楽ちん。
  23. instance_double, instance_spy $ gem install rspec-mocks $ pry pry(main)> require

    'rspec/mocks/standalone' => true pry(main)> instance_double(String, length: 100000000).length => 100000000 pry(main)> instance_double(String, length: 100000000).downcase RSpec::Mocks::MockExpectationError: #<InstanceDouble(String) (anonymous)> received unexpected message :downcase with (no args) pry(main)> instance_spy(String, length: 100000000).length => 100000000 pry(main)> instance_spy(String, length: 100000000).downcase => #<InstanceDouble(String) (anonymous)>
  24. 発表後の議論1: テストをDRYにすべきかどうか? DRY反対派 • shared_examples, shared_contextを多用すると、テストが失敗した時に 前提条件を追いづらい • ただ、カスタムMatcherやメソッドを作って共通化するなどのことはする DRY賛成派(←自分はこっち)

    • コピペで大量コ⑲ドが差分に現れると(数百q数千単位)、まず読むのが辛い • 似たようなコ⑲ドの中に実は絶妙な差分があるかもしれないのを探すのがキツい • 検証内容(expect)の追加が少ないコ⑲ド追加で可能 • DRY化されたテストの失敗原因を追うのは辛いけど、共通化されてるぶん ポイントさえ見つければ、失敗を直すのも早い
  25. 発表後の議論2: メソッドコ⑲ルを検証すべきか? • すべきじゃない派 ◦ 内部実装に依存しすぎて、IN/OUTに関係ないロジックの変更でもテストが 落ちてしまうのは不便 ◦ テストのIN/OUTh副作用(DB/外部への通信など)さえ合ってれば 内部実装はそこまで見なくてもいいのでは

    • したい派(←自分だけ?) ◦ 一度はデバッグして確認することなので、その確認をコ⑲ドで行っている。 ◦ 自動化しておけば、次に変更するタイミングでも同じクオリティでテストされ、 問題が未然に防げる(んじゃないか) ◦ IN/OUTh副作用以外にどこでどう問題になるか分からない恐怖に対して とりあえずできるところまで細かくテストしている。