【问题标题】:Mocha: stubbing method with specific parameter but not for other parametersMocha:具有特定参数但不用于其他参数的存根方法
【发布时间】:2013-05-15 07:38:39
【问题描述】:

我只想在给定特定参数值时使用 Mocha 存根方法,并在给定任何其他值时调用原始方法。

当我这样做时:

MyClass.any_instance.stubs(:show?).with(:wanne_show).returns(true)

我得到一个

unexpected invocation for MyClass.show?(:other_value)

我也知道,在没有'with'调用的情况下编写模拟时我可以存根所有参数,然后给出我的特定模拟。但是我必须知道每次调用的返回值,事实并非如此:/

tldr; 有没有办法在存根中调用原始方法或只存根特定参数而保留其他参数?

【问题讨论】:

    标签: ruby-on-rails mocha.js testunit


    【解决方案1】:

    我今天花了一个小时试图让 Mocha 只允许我存根一个特定的会话变量,就像 Rspec allows with ease 那样。虽然我无法找到实现这项工作的方法,但我确实找到了一种可能对某些人有所帮助的解决方法,具体取决于具体情况。

    我的“解决方案”是在获取目标实例变量后删除会话存根:

    ActionDispatch::Request::Session.any_instance.stubs(:delete).returns(state).then.returns(nonce).then.with do |sym|
      ActionDispatch::Request::Session.any_instance.unstub(:delete) if sym == :login_nonce
      true
    end
    

    我在这里使用的技巧是,通过知道在为特定操作进行的前两次调用中将传递给session.delete 的参数,我可以在第二次delete 调用之后删除存根(对于@ 987654325@) 已生成,因此会话再次开始正常运行。

    像这样构造with 块的另一个潜在有用的方面是该块具有调用者的完整上下文,因此可以直接检查或提取块内的会话内容。也就是说,如果你想要一个测试来获取 blah 会话键的值,你应该能够编写类似

    ActionDispatch::Request::Session.any_instance.stubs(:[]).with do |key|
      @blah = session[key] if key == :blah
      true
    end
    

    据我所知,with 块总是必须返回 true,否则 Mocha 将抛出 Minitest::Assertion: unexpected invocation 异常,因为它不知道如果它已存根方法但传入的参数该怎么办与它可以处理的参数不匹配。根本问题似乎是,一旦在 any_instance 上调用 stubs,您就不能再让 Mocha 从实际的 session 实例返回值(与 Rspec 不同,它允许使用 and_call_original 回退到原始对象如上面链接的答案)。

    希望将来有人可以在其中一些想法的基础上构建出一个更优雅的答案,但由于已经过去了将近 8 年并且没有答案,我认为这可能是一个可用的起点。

    【讨论】:

    • 谢谢。我将此作为公认的答案,因为它至少在大多数情况下都有帮助。
    【解决方案2】:

    答案取决于您到底在测试什么。

    一些注意事项:

    1) 我总是避免使用stubs.any_instance。你可以在你的存根/模拟中具体化,这可以防止错误的测试阳性。

    2) 我更喜欢将间谍与存根一起使用,以主动断言某些东西已被调用。为此,我们使用bourne gem。另一种方法是使用模拟,它隐式测试是否正在调用某些东西(例如,如果它没有被调用,则会失败。

    所以你的类方法可能看起来像这样(注意,这是 RSpec 语法):

    require 'bourne'
    require 'mocha'
    
    it 'calls MyClass.show?(method_params)' do
      MyClass.stubs(:show?)
    
      AnotherClass.method_which_calls_my_class
    
      expect(MyClass).to have_received(:show?).with('parameter!')
    end
    
    class AnotherClass
      def self.method_which_calls_my_class
        MyClass.show?('parameter!')
      end
    end
    

    有很多存根/间谍示例here

    希望这会有所帮助。

    【讨论】:

    • 你没有展示如何避免使用any_instance。根据我的经验,这需要对实例化代码进行额外的模拟,这些代码可能同样也可能不同,甚至更容易出错和脆弱。
    • 另外,使用 rspec 语法也没有多大帮助。
    • 这个 gem 已经过时了
    • 我同意@roman - 我只使用 RSpec Mocks。 github.com/rspec/rspec-mocks
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-09
    • 2016-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多