Edited at

【Rails】まだValidatorのテストで消耗してるの?

More than 3 years have passed since last update.


Custom Validator

RailsにおけるCustom ValidatorについてはRuby on Rails Guidesや「てめえらのRailsはオブジェクト指向じゃねえ!まずはCallbackクラス、Validatorクラスを活用しろ!」などの記事に触れられているとおりである.

Modelはごちゃごちゃさせたくないし,Validatorとして切りだしちゃえば使い回しも効くよね!という話(ざっくり).


Validatorのテスト

Custom ValidatorのテストについてはRailsでCustom validatorをテストするにて言及されている.

特定のModelに依存したテストを書くのはおかしな話だからダミーのクラスを用意しよう!みたいな感じ.


let(:model_class) do

Struct.new(:url) do
include ActiveModel::Validations

def self.name
'DummyModel'
end

validates :url, full_http_url: true
end
end

- RailsでCustom validatorをテストする - Qiita


ただ,これを各Validator Spec毎に毎回書くのは面倒だなーと思う.サボりたい.


RSpec::ValidatorSpecHelper

ということで,Validatorのテストで手抜きするためのgemを作った(izumin5210/rspec-validator_spec_helper).

勝手にダミークラスを生成してvalidatesvalidates_withも設定してくれるというものになっている.

EachValidator継承クラスの場合は1つのカラムにしか依存しないので,letを利用してvalueに値を突っ込むことでvalidateしてくれる.

あとはis_expectedやワンライナーshould等でbe_valid/be_invalidを見てあげればよい(subjectにダミークラスをnewしたものが入ってる).

describe EmailValidator, type: :validator do

describe '#validate_each' do
context 'with invalid format address' do
let(:value) { 'kokoro.pyonpyon' }
it { is_expected.to_not be_valid }
end

context 'with valid foramt address' do
let(:value) { 'kokoro.pyonpyon@example.com' }
it { is_expected.to be_valid }
end
end
end

EachValidatorじゃない(Validatorを直接継承している)場合は,validationに関係するカラム名をattribute_namesとして渡してあげる.

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

値を宣言するだけでValidationのテストができるので,とても楽になる.


References