1

Class structure:

class Service1
  def foo
    puts 'foo'
  end
end

class Service2
  def bar
    3.times.each do 
      Service1.new().foo
    end
  end
end

I want to test that bar method of Service2 is called 3 times.

How to do it best in rspec3?

nuT707
  • 1,543
  • 4
  • 17
  • 27
  • This might be helpful https://stackoverflow.com/a/50218941/3868595 – stolarz Jan 27 '22 at 08:26
  • 2
    Just a side note: if you encounter problems with some method's testability - this is often a clear sign of the design issue. In this case, it's a hardcoded dependency. If you inject an instance of `Service1` into `Service2#bar` (or via `Service2` initializer on its instantiation) tests would be _very_ straightforward and much more robust. – Konstantin Strukov Jan 27 '22 at 09:00

2 Answers2

1

I would refactor the code to:

class Service1
  def self.foo
    new.foo
  end

  def foo
    puts 'foo'
  end
end

class Service2
  def bar
    3.times.each do 
      Service1.foo
    end
  end
end

And would then use a spec like this

describe "#bar" do
  let(:service1) { class_double("Service1") }

  it "calls Service1.foo three times" do
    expect(service1).to receive(:foo).exactly(3).times

    Service2.bar
  end
end
spickermann
  • 100,941
  • 9
  • 101
  • 131
1

You can achieve this by mocking new method

class Service1
  def foo
    puts 'foo'
  end
end

class Service2
  def bar
    3.times.each do 
      Service1.new().foo
    end
  end
end

Then the test:

let(:mocked_service) { instance_spy Service1 }

it "calls Service1.foo three times" do
  allow(Service1).to receive(:new).and_return mocked_service

  Service2.bar

  expect(mocked_service).to have_received(:foo).exactly(3).times
end

However, as mentioned in the comment - the necessity of using mocks is a first sign of flawed OO design, meaning that the problem you posted is merely a symptom. Refer to SOLID principles to find better design.

BroiSatse
  • 44,031
  • 8
  • 61
  • 86