I have a class that uses the command pattern to do a bunch of simple transformation steps in order. Data comes in as a data feed (in XML) and then is transformed through multiple 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)
etc.
I have unit tests for each class, and I have integration tests to verify the full flow, including that each class is called.
But I don't have any way to test the order they are called
I'd like some test so I can know: the Downloader is called first, then the Parser, then the Translator, etc.
This is in Ruby with Rspec 3.
I did find this: http://testpractices.blogspot.com/2008/07/ordered-method-testing-with-rspec.html but this is from 2008 and it's also really ugly. Is there a better way to test method execution order?
Thanks!
Running tests by their file or directory names is the most familiar way to run tests with RSpec. RSpec can take a file name or directory name and run the file or the contents of the directory. So you can do: rspec spec/jobs to run the tests found in the jobs directory.
RSpec is a testing tool for Ruby, created for behavior-driven development (BDD). It is the most frequently used testing library for Ruby in production applications. Even though it has a very rich and powerful DSL (domain-specific language), at its core it is a simple tool which you can start using rather quickly.
In RSpec, a stub is often called a Method Stub, it's a special type of method that “stands in” for an existing method, or for a method that doesn't even exist yet. Here is the code from the section on RSpec Doubles − class ClassRoom def initialize(students) @students = students End def list_student_names @students.
Mocking is a technique in test-driven development (TDD) that involves using fake dependent objects or methods in order to write a test. There are a couple of reasons why you may decide to use mock objects: As a replacement for objects that don't exist yet.
RSpec Mocks provides ordered
since at least RSpec 3.0:
You can use
ordered
to constrain the order of multiple message expectations. This is not generally recommended because in most situations the order doesn't matter and using ordered would make your spec brittle, but it's occasionally useful. When you use ordered, the example will only pass if the messages are received in the declared order.
Note that RSpec agrees with @spickermann that this is not a recommended practice. However, there are some cases when it is necessary, especially when dealing with legacy code.
Here is RSpec's passing example:
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 failing 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
I would argue that the order of method calls is not important and should not be tested. Important is the result of a method, not its internals. Testing the order of internal method calls (instead of just the result of the tested method) will make it harder to refactor a method later on.
But if still want to test the order then you might want to test that the methods are called with a mocked result of the methods called before:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With