我想使用 minitest Ruby 测试一个函数是否正确调用其他函数,但我无法从
doc中找到合适的
assert
进行测试。
源代码
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
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
使用 minitest,您可以使用
expect
方法来设置对模拟对象调用方法的期望,如下所示
obj = MiniTest::Mock.new
obj.expect :right
如果你想用参数和返回值设置期望,那么:
obj.expect :right, return_value, parameters
对于像这样的具体对象:
obj = SomeClass.new
assert_send([obj, :right, *parameters])
根据给定的示例,没有其他委托类,并且您要确保从同一类正确调用该方法。那么下面的代码片段应该可以工作:
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_used],并在存根块中断言它确实被调用并返回 true 值。存根其他意外方法只是为了确保它没有被调用。
注意:assert_send 将无法正常工作。请参考官方文档。
事实上,下面的语句会通过,但并不意味着它按预期工作:
assert_send([obj, :invoke_function, 'right'])
# it's just calling invoke_function method, but not verifying any method is being called
要存根和断言方法调用,请使用
MiniTest::Mock
。有两种使用方法:
test "return the mock object when calling the stubbed method" do
# the object you want to stub
obj = Book.new
mock = MiniTest::Mock.new
mock.expect :the_method_to_stub, "my cool return value"
obj.stub :method_that_gives_you_a_mock, mock do
x = obj.method_that_gives_you_a_mock
assert_equal x.the_method_to_stub, "my cool return value"
end
# assert that the method was called once
mock.verify
end
test "call the mock method when calling the stubbed method" do
# the object you want to stub
obj = Book.new
mock = MiniTest::Mock.new
# use :call to make the mock a callable
mock.expect :call, "my cool return value"
obj.stub :method_that_calls_the_mock, mock do
assert_equal obj.method_that_calls_the_mock, "my cool return value"
end
# assert that the method was called once
mock.verify
end
要使用
MiniTest::Mock
,您可能需要添加 require 'minitest/autorun'
来加载 MiniTest 常量。
最近我创建了一个 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 对象时,可以使用它们。
一般来说,您可以按如下方式进行:
mock = Minitest::Mock.new(delegator)
mock.expect(:delete, 'return value', ['param1'])
其中
delegator
是委托了意外方法的对象。
上述答案与以前版本的 Minitest 的变化:
MiniTest
更改为 MiniTest
(注意小写“t”)。Mock#expect
需要返回值参数。