Andrew
Andrew

Reputation: 1013

Rspec: How to expect received message without stubbing anything or changing anything behind the scenes

I have this Rspec test for a rails app

  describe '.move_probes_to_master_list' do
    let(:as) { create_list :a, 3, }
    let(:bs) { create_list :b, 3 }

    it 'sums the volumes' do

      active_as= [as[1], as[2]]
      b_ids = [2, 3]
      expect_any_instance_of(A)
        .to receive(:required_volume).twice.with(b_ids)
      expect_any_instance_of(A)
        .to receive(:update_move_to_master_list).twice

      expect(A.calculate_volume(active_as)).to eq(true)
    end
  end

Basically I call A.calculate_volume and inside this class method, I want to ensure that some member of the A class is recieving some other messages as well. I don't want to stub out those methods, I want them to run as normal, but I just want to verify that the methods are being called.

This is being run in a loop, so I don't know exactly what instances I'll be dealling with, but I want to make sure that both messages are called on some members (but not necessarily the same member both times) of the A class twice in total.

If I remove the expect_any_instance_of(A).to receive expectations everything runs fine and the test passes.

If I keep them, the method call fails and the test breaks.

I tried adding and_call_original but I feel like I'm shooting in the dark because the docs aren't clear on how these methods actually operate.

So how can I verify that an instance of some class recieves a message n times without changing anything else about the method call?

Am I missing the point of expect to receive here? It's not obvious to me why it would stub anything in the first place.

Upvotes: 7

Views: 9035

Answers (2)

andrew21
andrew21

Reputation: 640

I know this doesn't exactly answer your question "how to continue execution", but you should be able to break this test into three distinct tests and create a more clear set of tests while not having to worry about calling the original as such:

describe '.move_probes_to_master_list' do
  let(:as) { create_list :a, 3, }
  let(:active_as) { [as[1], as[2]] }
  let(:bs) { create_list :b, 3 }
  let(:b_ids) { [2, 3] }
  subject { A.calculate_volume(active_as) }

  it 'sums the volumes' do
      expect(subject).to eq(true)
  end

  it 'calls #required_volumen twice' do
    expect_any_instance_of(A)
      .to receive(:required_volume).twice.with(b_ids)
    subject
  end

  it 'calls updates_moves_to_master_list twice' do
    expect_any_instance_of(A)
      .to receive(:update_move_to_master_list).twice
    subject
  end
end

Upvotes: 1

Himmelspiegel
Himmelspiegel

Reputation: 882

You could spy on the method like this:

allow(A).to receive(:calculate_volume).and_call_original

Then you could test that calculate_volume has been called like this:

expect(A).to have_received(:calculate_volume)

This way the original method will be called and there would be no stubbing but spying.

Upvotes: 11

Related Questions