1

I'm trying to use Minitest mocks to verify a call to a method that expects keyword arguments; but, I'm getting this error:

ArgumentError: wrong number of arguments (given 4, expected 2..3)

This is my complete test:

  test ".toggle updates the category" do
    mock = Minitest::Mock.new
    mock.expect :update!, nil, [], {collapsed: true}
    CategoryAnalyzer.toggle_category(mock, true)
    mock.verify
  end

This is the method under test:

  def self.toggle_category(category, collapsed)
    category.update!(collapsed: collapsed)
  end

I see from minitest - mock - expect keyword arguments that Minitest should accept keyword arguments. Is my test set up wrong? Do I somehow have an old version of Minitest? (My Gemfile.lock lists minitest (5.18.0)) Any idea of what else I should check?

Zack
  • 6,232
  • 8
  • 38
  • 68

1 Answers1

1

https://rubyreferences.github.io/rubychanges/3.0.html#keyword-arguments-are-now-fully-separated-from-positional-arguments

This is how you're calling expect, with 4 arguments:

#           1         2    3   4
mock.expect :update!, nil, [], {collapsed: true}

This is what it expects:

#          1     2       3          keywords  block
def expect name, retval, args = [], **kwargs, &blk

Since ruby v3.0 {collapsed: true} is a positional argument. To turn it into keywords, you have to ** splat or just drop the braces:

#           1         2    3   keywords
mock.expect :update!, nil, [], collapsed: true

# or this
mock.expect :update!, nil, [], **{collapsed: true}
kw = {collapsed: true}
mock.expect :update!, nil, [], **kw

A simple demo:

def arg_or_kwarg(one = nil, two = nil, **kwargs)
  {one: one, two: two, kwargs: kwargs}
end

# pass argument #1
>> arg_or_kwarg({hash: :arg})
=> {:one=>{:hash=>:arg}, :two=>nil, :kwargs=>{}}

# pass keyword argument
>> arg_or_kwarg(kw: :arg)
=> {:one=>nil, :two=>nil, :kwargs=>{:kw=>:arg}}
Alex
  • 16,409
  • 6
  • 40
  • 56