Minitest 存根将块传递给模拟实例

Minitest stub passing block to mock instance

我偶然发现了一个奇怪的行为,但一直无法弄清楚我做错了什么。希望有人能赐教。

我在 Rails 应用程序的测试期间试图对 Redis 客户端进行存根。因此我使用的是 MockRedis gem。我用单一 class 方法 .create 创建了一个 RedisFactory class,我想用一个可选的 MockRedis 实例存根,如下所示:

def stub_redis(mock_redis = MockRedis.new)
    RedisFactory.stub :create, mock_redis { yield }
end

这没有用,总是抛出 ArgumentError in Foo#Bar wrong number of arguments (0 for 1)。一些进一步的调试表明,在 stub 块中调用 RedisFactory.create 'foo' 会导致错误 'foo' is no method on instance of MockRedis::Database.

但是,我已经能够通过以下代码片段解决这个问题,使用 lambda 函数 catch 传入参数:

def stub_redis(mock_redis = MockRedis.new)
    RedisFactory.stub(:create, ->(*_args) { mock_redis }) { yield }
end

有人可以解释这种行为吗?

截至目前,MiniTest 尝试通过检查它是否响应 call 来猜测传递的 val_or_callable 是否为 Proc,参见:

不幸的是,在这种特定情况下 Redis 以及传递的 MockRedis-实例都提供了一个通用的 call-方法来执行 Redis 命令,cf.:

您已经找到了正确的解决方法。在这种情况下,您唯一的机会是显式使用 stub.

的 proc-version

注意:有些社区使用 def call 作为 ServiceObjects in Ruby which may have a difficult time using minitest's stub. It is probably a good idea to open an issue in seattlerb/minitest 的模式。