Align a few freaks in any order

I want to test an iterator using rspec. It seems to me that the only possible damage match is yield_successive_args (according to https://www.relishapp.com/rspec/rspec-expectations/v/3-0/docs/built-in-matchers/yield-matchers ). Other matches are used only for a single crop.

But yield_successive_args fails if the assignment is in a different order than indicated.

Is there any method or good workaround for testing an iterator that gives in any order?

Something like the following:

 expect { |b| array.each(&b) }.to yield_multiple_args_in_any_order(1, 2, 3) 
+7
iterator yield ruby ruby-on-rails rspec
source share
2 answers

Here is the assistant that I came up with for this problem, it is quite simple and should work with a good degree of efficiency.

 require 'set' RSpec::Matchers.define :yield_in_any_order do |*values| expected_yields = Set[*values] actual_yields = Set[] match do |blk| blk[->(x){ actual_yields << x }] # *** expected_yields == actual_yields # *** end failure_message do |actual| "expected to receive #{surface_descriptions_in expected_yields} "\ "but #{surface_descriptions_in actual_yields} were yielded." end failure_message_when_negated do |actual| "expected not to have all of "\ "#{surface_descriptions_in expected_yields} yielded." end def supports_block_expectations? true end end 

I highlighted lines containing most of the important logic with # *** . This is a fairly simple implementation.

Using

Just put it in a file under spec/support/matchers/ and make sure you require it from the specifications that need it. Most of the time, people simply add a line like this:

 Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f} 

to their spec_helper.rb , but if you have a lot of support files, and they are not needed everywhere, it can get a little big, so you can only include it where it is used.

Then, in the specs themselves, use is like using any other compliant match:

 class Iterator def custom_iterator (1..10).to_a.shuffle.each { |x| yield x } end end describe "Matcher" do it "works" do iter = Iterator.new expect { |b| iter.custom_iterator(&b) }.to yield_in_any_order(*(1..10)) end end 
+1
source share

This can be solved in regular Ruby with a set of intersections of arrays:

 array1 = [3, 2, 4] array2 = [4, 3, 2] expect(array1).to eq (array1 & array2) # for an enumerator: enumerator = array1.each expect(enumerator.to_a).to eq (enumerator.to_a & array2) 

Intersection ( & ) will return the elements that are present in both collections, preserving the order of the first argument.

0
source share

All Articles