提问人:LondonAppDev 提问时间:11/14/2023 更新时间:11/16/2023 访问量:34
Ruby on Rails Minitest 在一次测试中进行多个模拟
Ruby on Rails Minitest multiple mocks on one test
问:
我正在使用 Minitest 和 Ruby on Rails 编写单元测试。
有时我需要同时嘲笑多件事。
例如,在测试触发用户通知的操作时,我可能希望一次性模拟两个外部服务器(例如:和)。SmsClient
EmailClient
我可以按如下方式执行此操作:
test 'my test case' do
SmsClient.stub :send, nil do
EmailClient.stub :send, nil do
get my_controller_url
end
end
end
这有效,但是我现在需要在同一次调用中再模拟一个类。
我担心我对这些调用的嵌套会失控。stub
问:有没有办法在不使用嵌套的情况下在这些服务上设置模拟?
注意:这些示例只是一个假设。
答:
1赞
3limin4t0r
11/14/2023
#1
我不知道是否有任何 Minitest 助手可以简化这一点,但这里有一些可以减少嵌套的核心 Ruby 代码。
stubs = [
proc { |block| proc { SmsClient.stub(:send, nil, &block) } },
proc { |block| proc { EmailClient.stub(:send, nil, &block) } },
]
tests = proc do
# ...
end
stubs.reduce(:<<).call(tests).call
# results in
# (stubs[0] << stubs[1]).call(tests).call
#
# aka
# stubs[0].call(stubs[1].call(tests)).call
#
# aka
# SmsClient.stub(:send, nil) do
# EmailClient.stub(:send, nil) do
# # ...
# end
# end
要理解这个答案,你必须知道:
some_method :a, :b do
# block code
end
也可以写成:
block = proc do
# block code
end
some_method(:a, :b, &block)
数组中的每个项目都是一个 proc,它接受一个块,并返回一个传递给另一个元素的新 proc
。subs
stubs.reduce(:<<)
将创建一个组合过程,将任何参数传递给最后一个元素,该过程的返回值将传递给它前面的元素。
double = proc { |n| n + n }
pow2 = proc { |n| n * n }
operations = [pow2, double]
operations.reduce(:<<).call(2) # the pow2 of the double of 2
# pow2.call(double.call(2))
# a = 2 + 2 = 4
# b = a * a = 4 * 4 = 16
#=> b = 16
stubs.reduce(:<<).call(tests) # does the same
# stubs[0].call(stubs[1].call(tests))
# a = proc { EmailClient.stub(:send, nil, &tests) }
# b = proc { SmsClient.stub(:send, nil, &a) }
#=> b = proc { SmsClient.stub(:send, nil, &proc { EmailClient.stub(:send, nil, &tests) }) }
请注意,这仍然是一个过程,因此我们需要再次调用最终返回值。b
.call
下图可帮助您了解上述代码中发生的情况:
你可以定义一个帮助程序来抽象出常见的逻辑:
def stub_all(*stubs, &test_block)
stubs.map! do |subject, *stub_args|
proc { |block| proc { subject.stub(*stub_args, &block) } }
end
stubs.reduce(:<<).call(test_block).call
end
stub_all(
[SmsClient, :send, nil],
[EmailClient, :send, nil],
) do
# ...
end
评论