alex kucksdorf
alex kucksdorf

Reputation: 2633

Minitest stub passing block to mock instance

I stumbled across a curious behaviour and haven't been able to figure out what I was doing wrong. I hope somebody can enlighten me.

I was trying to stub the Redis client during my tests in a Rails application. Therefore I was using the MockRedis gem. I have created a RedisFactory class with the single class method .create, which I wanted to stub with an optional MockRedis instance like so:

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

This did not work and always threw a ArgumentError in Foo#Bar wrong number of arguments (0 for 1). Some further debugging revealed that a call of RedisFactory.create 'foo' within the stub-block resulted in an error that 'foo' is no method on instance of MockRedis::Database.

However, I have been able to solve this problem with the following code snippet, using a lambda function to catch the incoming arguments:

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

Could anybody explain this behaviour?

Upvotes: 1

Views: 1282

Answers (1)

mkoertgen
mkoertgen

Reputation: 992

As of now MiniTest tries to guess if the passed val_or_callable is a Proc by checking whether it responds to call, cf.:

Unfortunately, in this specific case Redis as well as the passed MockRedis-instance both provide a generic call-method for executing Redis commands, cf.:

You already found the correct workaround. In this case, your only chance is to explicitly use the proc-version of stub.

Note: There are some communities using def call as a pattern with 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.

Upvotes: 1

Related Questions