Validatorのテストを限界まで手抜きする
TL;DR
- rspec-validator_spec_helperつかう
- テストをパラメタライズドテストに落としこむ
- rspec-parameterizedつかう
- しあわせ
RSpec::ValidatorHelper
rspec-validator_spec_helperというgemがある.
これが提供するRSpec::ValidatorSpecHelper
はvalidatorのテストを簡単化するためのものである.
このクラスは以下の機能を提供する.
- validate対象になるダミーのクラスの提供
- ダミークラスの
validates
,validates_with
の設定
describe NotOverlappedValidator, type: :validator do let(:attribute_names) { [:begin_at, :end_at] } let(:begin_at) { Time.parse("2014-12-24T12:00:00+09:00") } describe '#validate' do context 'when end_at is overlapped' do let(:end_at) { Time.parse("2014-12-24T09:00:00+09:00") } it { is_expected.to_not be_valid } end context 'when end_at is not overlapped' do let(:end_at) { Time.parse("2014-12-24T19:00:00+09:00") } it { is_expected.to be_valid } end end end
さらなる単純化
RSpec::ValidatorSpecHelper
はダミークラスを提供するのがメインだが,その副作用でテストコードをパラメタライズドテストに落としやすくなるというものがある.
Parameterized Test とは、まさに似たようなテストメソッドをテストデータだけ変えて複数件実行することができる手段で、多くの場合テスティングフレームワークの拡張機能として提供されています。
これであなたもテスト駆動開発マスター!?和田卓人さんがテスト駆動開発問題を解答コード使いながら解説します~現在時刻が関わるテストから、テスト容易性設計を学ぶ #tdd|CodeIQ MAGAZINE
Validatorは何かしらの入力に対してtrue/falseを出力するだけで副作用がないものがほとんどなので,そもそもパラメタライズドテストにしやすいという特徴がある.
RSpec::ValidatorSpecHelper
がテストコードを規約で縛る役割を果たすので,よりパラメタライズドテスト化しやすくなる.
そこで,先ほどのサンプルコードをrspec-parameterizedを利用してリファクタリングしてみる.
describe NotOverlappedValidator, type: :validator do
let(:attribute_names) { [:begin_at, :end_at] }
where(:begin_at_str, :end_at_str, :be_valid_or_invalid) do
[
["2014-12-24T12:00:00+09:00", "2014-12-24T09:00:00+09:00", be_invalid],
["2014-12-24T12:00:00+09:00", "2014-12-24T19:00:00+09:00", be_valid]
]
end
with_them do
let(:begin_at) { Time.parse(begin_at_str) }
let(:end_at) { Time.parse(end_at_str) }
it { is_expected.to be_valid_or_invalid }
end
end
このサンプルはパラメータが長いのでちょっとだけ見栄えが悪いが,結構スッキリしたと思う.
これはテストケースが増えれば増えるほど効果がある.
逆に言うと,テストケースを増やしてもテストの複雑さには全く影響を及ぼさない.
また,ActiveModel::EachValidator
継承クラスのテストにはより効果が高いような気がしなくもない.
Conclusion
パラメタライズドテストたのしい