第一次发文 不足望指正
在官方文档中https://www.rubydoc.info/gems/rspec-rails/1.3.4/Spec/Rails/Matchers,提到了
response.should be_success #passes if response.success?
response.should be_redirect #passes if response.redirect?
但并没有提着个方法是从哪里来的, 接下来就详细讲解下 假设单元测试中一个断言为
expect(my_api_get_user.request).to be_success
首先会动态定义 be_success 方法,这个是通过/.rvm/gems/ruby-2.3.1/gems/rspec-expectations-3.7.0/lib/rspec/matchers.rb:953 的 BE_PREDICATE_REGEX 来定义的
def method_missing(method, *args, &block)
case method.to_s
when BE_PREDICATE_REGEX
BuiltIn::BePredicate.new(method, *args, &block)
when HAS_REGEX
#....
然后紧接着就会运行这个刚刚动态定义完的 be_success 紧接着就触发下面的追溯链 (TraceBack stack):
matches? [be.rb:193] (RSpec::Matchers::BuiltIn::BePredicate)
handle_matcher [handler.rb:50] (RSpec::Expectations::PositiveExpectationHandler)
with_matcher [handler.rb:27] (RSpec::Expectations::ExpectationHelper)
handle_matcher [handler.rb:48] (RSpec::Expectations::PositiveExpectationHandler)
to [expectation_target.rb:65] (RSpec::Expectations::ExpectationTarget::InstanceMethods)
my_api_get_user_spec.rb:15
//....
最顶端的 RSpec::Matchers::BuiltIn::BePredicate#matches?重写了 matches?方法,重写 matches?方法是自定义 Matcher 的手段:
#actual在这里就是{Symbol}success?
def matches?(actual, &block)
@actual = actual #actual is the instnce of my_api_get_user
@block ||= block #block can be nil
predicate_accessible? && predicate_matches?
end
predicate_accessible?用于确认 method sucess 是否真的存在于 my_api_get_user predicate_matches?的实现方式
#/.rvm/gems/ruby-2.3.1/gems/rspec-expectations-3.7.0/lib/rspec/matchers/built_in/be.rb:190
def predicate_matches?
method_name = actual.respond_to?(predicate) ? predicate : present_tense_predicate
@predicate_matches = actual.__send__(method_name, *@args, &@block)
end
可以看到 actual.send(method_name, *@args, &@block) 就是相当于,send是 ruby 中元编程动态定义方法的核心
my_api_get_user.success?(*@args,&@block)
如果返回的是 true 的话,最终会在这里做判断 (可以在之前的回溯链中找到 handle_matcher [handler.rb:48]):
#/.rvm/gems/ruby-2.3.1/gems/rspec-expectations-3.7.0/lib/rspec/expectations/handler.rb:47
def self.handle_matcher(actual, initial_matcher, message=nil, &block)
ExpectationHelper.with_matcher(self, initial_matcher, message) do |matcher|
return ::RSpec::Matchers::BuiltIn::PositiveOperatorMatcher.new(actual) unless initial_matcher
matcher.matches?(actual, &block) || ExpectationHelper.handle_failure(matcher, message, :failure_message)
end
end
matcher.matches?(actual, &block) || ExpectationHelper.handle_failure(matcher, message, :failure_message)
如果 matcher.matches?(actual, &block) 返回为 true,则断言为 true,否则就调用 ExpectationHelper.handle_failure 来出来错误断言