【问题标题】:How to assert certain method is called with Ruby minitest framework?如何断言某些方法被 Ruby minitest 框架调用?
【发布时间】:2012-06-07 19:47:15
【问题描述】:

我想用 minitest Ruby 测试一个函数是否正确调用了其他函数,但是我找不到合适的 assertdoc 进行测试。

源代码
class SomeClass
  def invoke_function(name)
    name == "right" ? right () : wrong ()
  end

  def right
    #...
  end

  def wrong
    #...
  end
end
测试代码:
describe SomeClass do
  it "should invoke right function" do
    # assert right() is called
  end

  it "should invoke other function" do
    # assert wrong() is called
  end
end

【问题讨论】:

    标签: ruby unit-testing testing minitest


    【解决方案1】:

    Minitest 有一个特殊的.expect :call 用于检查是否调用了某个方法。

    describe SomeClass do
      it "should invoke right function" do
        mocked_method = MiniTest::Mock.new
        mocked_method.expect :call, return_value, []
        some_instance = SomeClass.new
        some_instance.stub :right, mocked_method do
          some_instance.invoke_function("right")
        end
        mocked_method.verify
      end
    end
    

    不幸的是,这个功能没有很好地记录。我从这里找到了它:https://github.com/seattlerb/minitest/issues/216

    【讨论】:

    • 很遗憾问题是如何关闭的。 return_value 在这里是什么意思?
    • return_value 是您希望mocked_method 返回的值(而不是调用原始的mocked_method 实现)
    【解决方案2】:

    在 minitest 中,您可以使用 expect 方法来设置要在模拟对象上调用的方法的期望,就像这样

    obj = MiniTest::Mock.new
    obj.expect :right
    

    如果你想用参数和返回值设置期望,那么:

    obj.expect :right, return_value, parameters
    

    对于像这样的具体对象:

    obj = SomeClass.new
    assert_send([obj, :right, *parameters])
    

    【讨论】:

    • 具体对象版本,使用minitest/spec时有must_send
    • 如果我遗漏了什么,请告诉我,但我认为下面@jing-li 的答案是正确的,因为提到assert_send 在这里不合适。 OP 想要测试在调用 invoke_function 时是否调用了 right。此外,需要obj.verify 以确保在此答案的第一部分中对模拟对象调用right。对于阅读本文的任何人,请确保您的测试在通过之前失败。
    • 我认为使用 assert_send 不是一个安全的选择。根据文档:ruby-doc.org/stdlib-2.0.0/libdoc/minitest/rdoc/MiniTest/…
    • 您能否详细说明您的示例何时调用invoke_function
    • 答案是完全不正确的,使用给定的代码,您从未测试过正确的调用。 assert_send 只是在执行正确的方法,与assert( obj.right(*args) ) 相同,并且对模拟对象的期望也是不正确的建议,即使用模拟对象,您需要用模拟程序替换代码中某处的原始对象。
    【解决方案3】:

    根据给定的示例,没有其他委托类,并且您要确保从同一类正确调用该方法。然后下面的代码 sn-p 应该可以工作:

    class SomeTest < Minitest::Test
      def setup
        @obj = SomeClass.new
      end
    
      def test_right_method_is_called
        @obj.stub :right, true do
          @obj.stub :wrong, false do
            assert(@obj.invoke_function('right'))
          end
        end
      end
    
      def test_wrong_method_is_called
        @obj.stub :right, false do
          @obj.stub :wrong, true do
            assert(@obj.invoke_function('other'))
          end
        end
      end
    end
    

    这个想法是通过返回一个简单的 true 值来存根 [method_expect_to_be_call],并在存根块中断言它确实被调用并返回 true 价值。存根另一个意外方法只是为了确保它没有被调用。

    注意:assert_send 无法正常工作。请参考official doc

    事实上,下面的语句会通过,但并不意味着它按预期工作:

    assert_send([obj, :invoke_function, 'right'])
    # it's just calling invoke_function method, but not verifying any method is being called
    

    【讨论】:

      【解决方案4】:

      最近我创建了一个 gem 来缓解这种断言,称为'stubberry'

      在这里,您可以如何使用它来管理所需的行为。

      首先你需要回答问题:

      • 您是否可以在测试序列之前访问原始对象 执行?

      • 有什么间接的方法可以确定通话发生了吗?即,您有权访问的其他对象上应该有一些方法调用。

      • 您是否需要实际调用该方法,或者是否可以将其与适当的对象或可调用对象一起存根?

      如果您有权访问该对象,并且可以使用您的可调用对象存根该方法:

      obj.stub_must( :right, -> { stub_response } ) {
        ... do something 
      }
      

      如果您有权访问该对象,但您不想存根该方法并且只想确保调用该方法:

        assert_method_called( obj, :right ) {
          .... do something with obj
        }
      

      如果您无权访问要测试的对象。 但是您可以使用其他一些对象方法调用进行间接检查,假设“正确”方法将以 API 调用执行结束:

      API.stub_must( :get, -> (path, params) {
        assert_equal( path, :expected_path )
        assert_equal( params, {} )
      } ) do
        ... do something  
      end
        
      

      你不能做间接检查:

      
      stunt_boudle = Obj.new
      
      stunt_boudle.stub_must( :right, -> () {
        #do needed assertions
      } ) do
         Obj.stub_must(:new, stunt_boudle) do 
           # do some integration testing 
         end
      end 
      
      # OR use assert_method_called the same way
      
      

      还有一组很酷的通过 id 存根的 ActiveRecord 对象,当您在测试操作开始时无法访问该对象及其 ActiveRecord 对象时,您可以在这种情况下使用它们。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-11-18
        • 1970-01-01
        • 2022-01-21
        • 1970-01-01
        • 2021-11-16
        • 2016-09-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多