Reference: RSpec Custom Matchers

You can create custom matchers for your testing.

Let’s say that you want to test if a hash (hash_a) contains another hash (hash_b).

At the end of your test file, add the following matcher:

it "contains hash" do
    hash_a = {bruno: "monteiro"}
    hash_b = {bruno: "monteiro"}
    expect(hash_a).to contains_hash(hash_b)
end

RSpec::Matchers.define :contains_hash do |hash_b|
    match do |hash_a|
        # >= is a ruby method to check if a hash contains another
        hash_a >= hash_b
    end
end

But hash_a and hash_b are not the greatest names for variables within your matcher. In this case, hash_a is going to be the expected_hash and hash_b is going to be the actual_hash

...

RSpec::Matchers.define :contains_hash do |expected_hash|
    match do |actual_hash|
        # >= is a ruby method to check if a hash contains another
        actual_hash >= expected_hash 
    end
end

Using diffable

Now you have a working matcher, but is the output of your matcher good? Let’s make it fail:

it "contains hash" do
    hash_a = {bruno: "monteiro"}
    hash_b = {bruno: "rocha"}
    expect(hash_a).to contains_hash(hash_b)
end

with_diffable

It is easy to see why it’s failing because our hash is small, but if the hash was larger, it would be difficult to see what is wrong with your test. So, you can use a built-in function in Rspec: diffable.

...

RSpec::Matchers.define :contains_hash do |expected_hash|
    match do |actual_hash|
        # >= is a ruby method to check if a hash contains another
        actual_hash >= expected_hash
    end

    diffable
end

with_diffable

The tests are looking pretty good now.

Using chain

Now let’s say that you have a hash of hashes and you want to know if that hash contains a certain hash. You can use the built-in RSpec function chain:

it "contains hash" do
    hash_a = {first_name: {bruno: "monteiro"}, second_name: {rocha: "lima"}}
    hash_b = {bruno: "rocha"}
    expect(hash_a).to contains_hash(hash_b).within(:first_name)
end

RSpec::Matchers.define :contains_hash do |expected_hash|
    match do |actual_hash|
        # >= is a ruby method to check if a hash contains another
        actual_hash >= expected_hash
    end

    diffable
    
    chain :within do |hash_value|
        @hash_value = hash_value
    end
end