How to check the order of method calls in rspec?

I have a class that uses a command template to complete a sequence of simple conversion steps. Data is supplied as a data feed (in XML) and then converted using several steps using single-purpose step classes. So it might look like this (actual class names are different):

raw_data = Downloader.new(feed) parsed_data = Parser.new(raw_data) translated_data = Translator.new(parsed_data) sifted_data = Sifter.new(translated_data) collate_data = Collator.new(sifted_data) 

and etc.

I have unit tests for each class, and I have integration tests for checking the full flow, including calling each class.

But I have no way to check the order they call

I need some test so that I can know: Downloader is called first, then Parser, then Translator, etc.

This is in Ruby with Rspec 3.

I found this: http://testpractices.blogspot.com/2008/07/ordered-method-testing-with-rspec.html , but this is from 2008 and it is also very ugly. Is there a better way to check the execution order of a method?

Thanks!

+5
source share
2 answers

RSpec Mocks provides ordered since at least RSpec 3.0:

You can use ordered to limit the waiting order of multiple messages. This is usually not recommended, because in most cases the order does not matter, and using the order should make your spec fragile, but it is useful from time to time. When you use ordered, an example will be transmitted only if messages are received in the declared order.

Please note that RSpec agrees with @spickermann that this is not recommended. However, there are some cases where this is necessary, especially when working with legacy code.

Here is an example RSpec transfer:

 RSpec.describe "Constraining order" do it "passes when the messages are received in declared order" do collaborator_1 = double("Collaborator 1") collaborator_2 = double("Collaborator 2") expect(collaborator_1).to receive(:step_1).ordered expect(collaborator_2).to receive(:step_2).ordered expect(collaborator_1).to receive(:step_3).ordered collaborator_1.step_1 collaborator_2.step_2 collaborator_1.step_3 end end 

And unsuccessful examples:

 RSpec.describe "Constraining order" do it "fails when messages are received out of order on one collaborator" do collaborator_1 = double("Collaborator 1") expect(collaborator_1).to receive(:step_1).ordered expect(collaborator_1).to receive(:step_2).ordered collaborator_1.step_2 collaborator_1.step_1 end it "fails when messages are received out of order between collaborators" do collaborator_1 = double("Collaborator 1") collaborator_2 = double("Collaborator 2") expect(collaborator_1).to receive(:step_1).ordered expect(collaborator_2).to receive(:step_2).ordered collaborator_2.step_2 collaborator_1.step_1 end end 
+4
source

I would say that the order of method calls is not important and should not be tested. Important is the result of the method, not its internal elements. Testing the order of calls to internal methods (and not just the result of the tested method) will complicate the method reorganization later.

But if you still want to test the order, you may need to verify that the methods are called with the mocked result of the previously called methods:

 let(:raw_data) { double(:raw_data) } let(:parsed_data) { double(:parsed_data) } # ... before do allow(Downloader).to_receive(:new).and_return(raw_data) allow(Parser).to_receive(:new).and_return(parsed_data) # ... end it 'calls method in the right order' do foo.bar # the method you want to test expect(Downloader).to have_received(:new).with(feed) expect(Parser).to have_received(:new).with(raw_data) # ... end 
+2
source

All Articles