Raymond Peterfellow
Raymond Peterfellow

Reputation: 644

How to test a method is invoked after other in an object?

I have the following class:

class Foo
  def initialize(foobar:)
    @foobar = foobar
  end

  def call
    return if foobar.blank?
    do_something_here
  end
end

Which is being invoked in an after_commit callback from a model:

included { after_commit :invoke_method_in_poro, on: %I[create update] }

private

def invoke_method_in_poro
  Foo.new(foobar: to_json).call
end

Now when I try to test if call is being invoked, I need to first stub the new method, because I'm getting this error:

NameError: undefined method `call' for class `Foo'
Did you mean?  caller

With this code:

foo = Foo
new_mock = Minitest::Mock.new
new_mock.expect :call, nil
foo.stub(:call, new_mock) do
  create(:model)
end
mock.verify

My question is, how finally can I test that call is being invoked?

Upvotes: 1

Views: 378

Answers (1)

Chris Heald
Chris Heald

Reputation: 62688

You're running into a peculiarity of Minitest here: when call is defined on a method, it appears that something in the minitest stack attempts to call it when an object is defined.

Given this test setup:

require 'minitest/autorun'

class Foo
  def call
  end
end

class Bar
  def test
    Foo.new.call
  end
end

describe "Bar" do
  it "invokes foo.call" do
    mock = Minitest::Mock.new(Foo)
    mock.expect(:call, nil)

    Foo.stub :new, mock do |args|
      Bar.new.test
      mock.verify
    end
  end
end

It fails as described. But if you rename call to my_call, it passes:

require 'minitest/autorun'

class Foo
  def my_call
  end
end

class Bar
  def test
    Foo.new.my_call
  end
end

describe "Bar" do
  it "invokes foo.my_call" do
    mock = Minitest::Mock.new(Foo)
    mock.expect(:my_call, nil)

    Foo.stub :new, mock do |args|
      Bar.new.test
      mock.verify
    end
  end
end

The same test passes under RSpec:

class Foo
  def call
  end
end

class Bar
  def test
    Foo.new.call
  end
end

describe "Bar" do
  it "invokes foo.call" do
    mock = double
    expect(mock).to receive(:call)
    expect(Foo).to receive(:new).and_return(mock)
    Bar.new.test
  end
end

Upvotes: 1

Related Questions