まず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