Cover all test cases with #permutation

… and check why 5600+ Rails engineers read also this

Cover all test cases with #permutation

When dealing with system which cooperate with many other subsystems in an asynchronous way, you are presented with a challenge. Due to the nature of such systems, messages may not arrive always in the same order. How do you test that your code will react in the same way in all cases?

Let me present what I used to be doing and how I changed my approach. The example will be based on a saga but it applies to any solution that you want to test for order independence.

specify "postal sent via API" do
  procs = [
    ->{ postal.call(fill_out_customer_data) },
    ->{ postal.call(paid_data) },
    ->{ postal.call(tickets_generated_data) },
  ].shuffle

  procs[0].call
  procs[1].call

  expect(api_adapter).to receive(:transmit)
  procs[2].call
end

This solution however has major drawbacks

  • It does not test all possibilities
  • Failures are not easily reproducible

It will eventually test all possibilites. Given enough runs on CI. And you can reproduce it if you pass the --seed attribute.

But generally it does not make our job easier. And it might miss some bugs until it is executed enough times.

It was rightfully questioned by Paweł, my coworker. We can do better.

#permutation

We should strive to test all possible cases. It’s boring to go manually through all 6 of them. With even more possible inputs the number goes high very quickly. And it might be error prone. So let’s generate all of them with the little help of #permutation method.


[
  fill_out_customer_data,
  paid_data,
  tickets_generated_data,
].permutation.each do |fact1, fact2, fact3|
  specify "postal sent via API when #{[fact1.class, fact2.class, fact3.class].to_sentence}" do
    postal.call(fact1)
    postal.call(fact2)

    expect(api_adapter).to receive(:transmit)
    postal.call(fact3)
  end
end

Caveats

  • The more cases you generate the faster they should run individually
  • There is obviously a certain limit after which doing this does not make sense anymore. Maybe in such case fuzzy testing or moving it outside the main build is a better solution.

If you enjoyed this blog post you will like our books as well.

You might also like