I have written a custom match method in Rspec that matches an object against a hash. What I am trying to do is set custom failure messages for each line of the expect
.
describe "/cars" do
car = FactoryGirl.create(:car, name: 'Alpha')
describe car do
it "displays single items" do
get cars_path
parsed_response = JSON.parse(response.body)
record_hash = parsed_response['cars'][0]
is_expected.to be_a_car_match_of(record_hash)
end
end
end
RSpec::Matchers.define :be_a_car_match_of do |hash|
match do |car|
expect(car.id).to eq(hash['id'])
expect(car.name).to eq(hash['name'])
end
failure_message do |car|
"expected that #{car} would be a match of #{hash}"
end
end
So what I would like is to have something like the following:
RSpec::Matchers.define :be_a_car_match_of do |hash|
match do |car|
expect(car.id).to eq(hash['id']) 'ids did not match'
expect(car.name).to eq(hash['name']) 'names did not match'
end
end
This would print out a much clearer error message.
I was initially doing this in mini-test but for a variety of reasons (outside of my control) needed to change it to rspec. The code I had in mini-test was:
def assert_car(car, hash)
assert_equal car.id, hash['id'], "ids did not match"
assert_equal car.name, hash['name'], "names did not match"
end
This is what I am trying to replicate.
Here is another example that requires less setup:
require 'rspec/expectations'
RSpec::Matchers.define :be_testing do |expected|
match do |actual|
expect(5).to eq(5)
expect(4).to eq(5)
end
failure_message do
"FAIL"
end
end
describe 'something' do
it 'something else' do
expect("expected").to be_testing('actual')
end
end
When this example is run, "FAIL" is printed out. On the other hand if I had:
describe 'something' do
it 'something else' do
expect(4).to eq(5)
end
end
I would get the following error message:
expected: 5
got: 4
This is what I want. I want to know what part of the custom matcher failed.
You could call the low-level matches?
method instead of expect
. Something like this:
require 'rspec/expectations'
RSpec::Matchers.define :be_a_positive_integer do
m1, m2 = nil, nil # matchers
r1, r2 = false, false # results
match do |actual|
m1 = be_a Integer # this returns matcher instances
m2 = be > 0
r1 = m1.matches?(actual) # evaluate matchers
r2 = m2.matches?(actual)
r1 && r2 # true if both are true
end
failure_message do |actual| # collect error messages from matchers
messages = []
messages << m1.failure_message unless r1
messages << m2.failure_message unless r2
messages.join("\n")
end
end
describe -1 do
it { is_expected.to be_a_positive_integer }
end
describe 1.0 do
it { is_expected.to be_a_positive_integer }
end
describe -1.0 do
it { is_expected.to be_a_positive_integer }
end
Output:
Failures:
1) -1 should be a positive integer
Failure/Error: it { is_expected.to be_a_positive_integer }
expected: > 0
got: -1
# ./ruby_spec.rb:24:in `block (2 levels) in <top (required)>'
2) 1.0 should be a positive integer
Failure/Error: it { is_expected.to be_a_positive_integer }
expected 1.0 to be a kind of Integer
# ./ruby_spec.rb:28:in `block (2 levels) in <top (required)>'
3) -1.0 should be a positive integer
Failure/Error: it { is_expected.to be_a_positive_integer }
expected -1.0 to be a kind of Integer
expected: > 0
got: -1.0
# ./ruby_spec.rb:32:in `block (2 levels) in <top (required)
I think aggregate_failures
is what you are looking for :
It wraps a set of expectations with a block. Within the block, expectation failures will not immediately abort like normal; instead, the failures will be aggregated into a single exception that is raised at the end of the block, allowing you to see all expectations that failed.
See : https://relishapp.com/rspec/rspec-expectations/docs/aggregating-failures
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