4 Jun 2012

RSpecのshouldは何をしてるの? RSpecの仕組み

RSpecのshouldはどうやって動いているのか?..という仕組みについてpaperboy&co.の方が既にcodeを読んで解説されているスライドを見つけました。

まずshouldが全てのinstaceで実効可能なのはKernel classに対して定義されているからです。

shouldの居場所
# spec/expectations/extensions/kernel.rb
def should(matcher=nil, message=nil, &block)
  Spec::Expectations::PositiveExpectationHandler.handle_matcher(self, matcher, message, &block)
end

PositiveExpectationHandler#handle_matcherの居場所
# spec/expectations/handler.rb
match = matcher.matches?(actual, &block)



RSpecの構造

/lib
└── spec
    ├── expectations
    │   └── extensions
    └── matchers
        └── extensions

expectations/extensionsはその名のとおり拡張を行う。spec/expectations/extensions/にはkernel.rbのみがある

RSpecの構造
spec/matchersには見慣れた名前が。。

ずっとshouldはきっと variable.be_a Stringてかくと variable.is_a? Stringって変えてくれると想像して信じていたけど、実際の条件は逐一Matcherに書いてあるらしい。例えばbe_kind_of matcher
def be_a_kind_of(expected)
  Matcher.new :be_a_kind_of, expected do |_expected_|
    match do |actual|
      actual.kind_of?(_expected_)
    end
  end
end

be Matcherには僕が想像していたような機能がある

§

Matcherを自作したい時は Object.should be_fine => Object.fine?の法則に当てはまるfine? methodを持つObject定義してあげるか、matches?にrespondするMatcherを書いてmatcher.rbみたいに requireしてあげればいいはず。


§


蛇足だけど

上記 kernel.rbのとおり引数にmessageを渡してfailure時に何が失敗したのかわかりやすくすることもできる

別のshould、Subject#shouldの居場所
これは以下の用に書いたときのshouldにちがいない(と信じてる)
let(:int) { 16 }
subject { int }

it "should be even" do
  should be_even
end


Change

例えばこんな感じに書いたときは
expect{ array << 42 }.to change{ array.size }.from(0).to(1)
initializeで @value_proc に{ array << 42 }が代入されてfrom methodで@fromに0が、to methodで@to に1が代入される。比較は他のmatchers同様matches?で @beforeと@afterを比較して行われる。event_procには array.sizeが代入される。
@before = evaluate_value_proc
event_proc.call
@after = evaluate_value_proc
スライドすばらしいので全部読むべし