5

主要用于测试调试目的,我想知道是否有办法判断方法是否被存根。

例如,在我的一项测试中,我写道:

Model.any_instance.stub(:method)

当我有一个真实的实例时,我想写一些类似的东西:

an_instance_of_model.stubbed?(:method) # expecting to return true
4

2 回答 2

4

如果您在类级别使用该any_instance方法存根该方法,您可以在测试中使用类似这样的方法进行检查:

RSpec::Mocks.space.any_instance_recorder_for(YourClass).already_observing?(:method_name)

但是如果你对一个特定的实例进行了存根,你可以使用以下方法找到它:

!(your_instance.method(:method_name).owner >= your_instance.class)

因此,您也可以将它们组合在一个辅助模块中:

module Helpers
  def stubbed?(object, method)
    RSpec::Mocks.space.any_instance_recorder_for(object.class).already_observing?(method) || !(object.method(method).owner >= object.class)
  end
end

并将其包含在您的 RSpec.configure 中:

require 'helpers'
RSpec.configure do |config|
  ...
  config.include Helpers
  ...
end

注意:这已根据@PJSCopeland 的评论进行了更新。如果您使用的 RSpec 版本低于 3.6,请删除.space调用,即RSpec::Mocks.any_instance_recorder_for(YourClass).already_observing?(:method_name)

于 2013-12-09T13:55:48.357 回答
2

tl;博士

虽然它似乎没有很好地记录但从@PJSCopeland 的评论开始,您似乎可以使用它RSpec::Mocks.space.registered?来检查“模拟代理”是否存在:

# eg. allow(Rails.configuration.action_controller).to receive(:allow_forgery_protection).and_return(true)

RSpec::Mocks.space.registered?(Rails.configuration.action_controller)
# => true

RSpec::Mocks.space.registered?(Rails.configuration)
# => false

更深入

在 pry 调试会话中,我们可以看到:

ls RSpec::Mocks
# => RSpec::Mocks.methods: allow_message  configuration  error_generator  expect_message  setup  space  teardown  verify  with_temporary_scope

ls RSpec::Mocks.space
# => RSpec::Mocks::Space#methods: any_instance_mutex  any_instance_proxy_for  any_instance_recorder_for  any_instance_recorders  any_instance_recorders_from_ancestry_of  constant_mutator_for  ensure_registered  new_scope  proxies  proxies_of  proxy_for  proxy_mutex  register_constant_mutator  registered?  reset_all  superclass_proxy_for  verify_all

查看https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/space.rb#L127-L129我们可以看到registered?checks proxies.key?,类proxies的属性在哪里RSpec::Space

module RSpec
  module Mocks
    # ..snip..
    class Space
      attr_reader :proxies, :any_instance_recorders, :proxy_mutex, :any_instance_mutex
      # ..snip..
      def registered?(object)
        proxies.key?(id_for object)
      end
      # ..snip..
    end
  end
end

在 pry 中,我们可以看到这proxies是一个Hash将 id 号(由 创建id_for)映射到RSpec::Mocks::Proxy表示该模拟的实例:

RSpec::Mocks.space.proxies.class
# => Hash

RSpec::Mocks.space.proxies.map { |k,v| v.class }
# => [RSpec::Mocks::PartialClassDoubleProxy, RSpec::Mocks::PartialDoubleProxy]

正如您从可用方法中看到的那样,如果我们愿意,这将使我们能够非常深入地使用模拟:

ls RSpec::Mocks.space.proxies.map { |k,v| v }.first
# RSpec::Mocks::Proxy#methods: add_message_expectation  add_stub  as_null_object  build_expectation  check_for_unexpected_arguments  ensure_implemented  has_negative_expectation?  messages_arg_list  method_double_if_exists_for_message  null_object?  object  prepended_modules_of_singleton_class  raise_missing_default_stub_error  raise_unexpected_message_error  received_message?  record_message_received  remove_stub  remove_stub_if_present  replay_received_message_on  verify
# RSpec::Mocks::PartialDoubleProxy#methods: add_simple_expectation  add_simple_stub  message_received  reset  visibility_for
# RSpec::Mocks::PartialClassDoubleProxyMethods#methods: original_method_handle_for

示例:重置模拟值

例如,如果我们想将reset这个 mock 恢复为它的 unmocked 默认值,我们可以使用RSpec::Mocks.space.proxy_forhelper 来找到我们的 mock,然后reset它:

# when
#   Rails.configuration.action_controller.allow_forgery_protection == false
# and
#   allow(Rails.configuration.action_controller).to receive(:allow_forgery_protection).and_return(true)

RSpec::Mocks.space.registered?(Rails.configuration.action_controller)
# => true

Rails.configuration.action_controller.allow_forgery_protection
# => true

RSpec::Mocks.space.proxy_for(Rails.configuration.action_controller).reset

Rails.configuration.action_controller.allow_forgery_protection
# => false

但是请注意,即使模拟值已被重置,模拟仍然存在registered?

RSpec::Mocks.space.registered?(Rails.configuration.action_controller)
# => true
于 2019-11-20T03:41:28.510 回答