ランダム性は心の弱さ -- Rails においてテストデータをランダムに作ることについて

お気持ち

やめよう

何がおきたか

純化するとこんな感じのコードがあって爆死した1

describe Foo do
  let(:left_foo) { Foo.new(value: 1000, bar: left_bar) }
  let(:left_bar) { BarMaster.all.sample }
  let(:right_foo) { Foo.new(value: 100, bar: right_bar) }
  let(:right_bar) { BarMaster.all.sample }

  it { expect(left_foo).to > right_foo } # left と right の bar が同じであるケースのとき落ちる
end

あるいはこんなでもいい

describe Foo do
  let(:left_foo) { Foo.new(value: rand(100)) }
  let(:right_foo) { Foo.new(value: 0) }

  it { expect(left_foo).to > right_foo } # rand(100) の値域が 0..99 なので 0 を引くと落ちる
end

なぜ避けるべきか

テストというのは書けばよいものではない。動作を担保し、オブジェクトに対する期待を明らかにするものである。
どのデータでも通ることを期待するならすべてのデータについてテストを回すべきだし、そうでないならそのテストで期待するものを満たす最小限のもので回すべきだ。
テストパターンにおけるランダム性の導入は、そうした最小の期待への考慮を放棄している、あるいは対象を正しく理解できていないように感じられる2
なお QuickCheck のようなものを否定するわけではない。しかし QuickCheck のようなものは仕組みとして提供されるほうが望ましく、 Rails アプリケーションにおいてそのようなテストフレームワークを使っているのは稀だと思う。というか例があるなら聞きたい

実際には request spec がこのように書かれており、request spec で何を担保するのかというところが曖昧なようにも見えた。

お気持ち再掲

ランダムなデータにするのはやめて代表値を選んで使おう。


  1. 業務で出会ったコードで業務臭を消して単純化するのは難しい

  2. 個人の感想です