A Guide To RSpec Built-in Matchers Part 1

Equality & Comparison Matchers

Erik Huang
5 min readJun 21, 2021

Click here for Part 2

Click here for Part 3

In this series of blogs, we will be going through some of RSpec’s most useful built-in matchers for writing assertions in examples. RSpec is known for its beautiful english-like syntax and matchers are no exception. Whether you’re completely new to testing in Ruby or a unit testing veteran, this guide aims to be a comprehensive introduction/review on some of the most common built-in matchers available to the RSpec library.

In this first part, we will be taking a look at two of the most commonly-used families of matchers: equality and comparison. Before we dive into those two groups however, let’s take a quick second to first talk about the to and not_to methods available in RSpec.

The to & not_to Method

This first method is a bit of an outlier within this blog, but is one of the most straight-forward to use. If you have had any experience with RSpec, chances are you have some familiarity with writing assertions using expect(...) followed by a call to the to method.

expect(number).to eq(9)

This creates a basic assertion expecting a variable called number to have a value of 9. The not_to method is simply an alternative that operates similarly to a ! (bang) symbol within most programming languages: negating the boolean value it precedes.

expect(number).not_to eq("String")

In plain english: the value of the variable number is not equal to the string "String". I find that a great way to practice writing tests is to always try and construct a not_to method assertion that exactly mirrors what another to method is checking for.

full_name = "Goro Majima"expect(full_name).to start_with("Goro")expect(full_name).not_to start_with("Kazuma")

Equality Matchers

Now, on to built-in matchers that come afterto / not_to methods! The first set of three are equality matchers that check the value and identity of a subject.

eq & eql Matchers

When first learning RSpec, most developers like myself become very comfortable with the eq matcher, that checks for value equality between a subject and its expectation. Using eq, we can expect that 2is eqto 2, the length of the string "Hello"is eq to the length of the string "World", or that "racecar"is eqto "racecar".reverse.

Each of these tests pass using the eq matcher

eql works very similarly to eq, but it checks for value AND type equality. In other words, an integer cannot be eql to its exact equivalent as a float, or a string containing the integer. An assertion must match the data type of its subject.

The eql matcher checks that the values AND types are the same

Most of the time, as long as you have a good sense of what your subject’s data type should be, eql matchers provide an extra bit of security that your business logic is working the way it should be. el in contrast, provides you with the flexibility of allowing type conversions (5 == 5.0).

equal & be Matchers

A different form of equality in Ruby is the concept of object identity. Whenever a reference type is initialized in RSpec, that instance is assigned a unique ID representing its place in memory. This means that any reference type in Ruby (arrays, hashes, or class instances) possess this unique identifier: an array will not be identical to an array with the same exact values unless they both reference the same position in memory.

RSpec’s equal and be matchers are aliases of each other (they perform the same function). Both will check a reference data type for object identity to make sure they point to the same object. Let’s use hashes as an example:

We still need to fill out our example logic, but here we have initialized two variables representing hashes using the let keyword. What happens if we make an assertion using equal to compare first_hash to second_hash?

Running this test results in the following error:

Failures:1) equal & be matchers checks for object identity
Failure/Error: expect(first_hash).to equal(second_hash)

expected #<Hash:2780> => {:a=>1, :b=>2, :c=>3}
got #<Hash:2840> => {:a=>1, :b=>2, :c=>3}

Compared using equal?, which compares object identity,
but expected and actual are not the same object. Use
`expect(actual).to eq(expected)` if you don't care about
object identity in this example.


Diff:
<The diff is empty, are your objects producing identical `#inspect` output?>
# ./spec/eql_spec.rb:18:in `block (2 levels) in <top (required)>'

The paragraph underneath expected/got highlights the exact reason our test failed. Even though they contain the exact same key/value pairs, first_hash is not equal to second_hash because their pointers are referencing two completely different hashes in memory. This can clearly be seen in the expected/got lines as well.

In order to allow our test to pass, we need to add in a third variable using let that points to the same position in memory as one of the other variables, and use eq or eql to compare our hashes otherwise.

Unless two references point to the same place in memory, the equal matcher will fail.

Now all of our tests in this example group should be passing! Keep in mind that any call to equal can be replaced with the be keyword instead. Later we will see ways in which be is used together with other types of matchers as well.

Remember these guidelines when using equality matchers:

  • eq checks for value equality only
  • eql checks for value and data type equality
  • equal/be checks for object identity

If you keep these rules in mind and use equality matchers as they are appropriate, you’ll be one step closer to mastering RSpec testing!

Comparison Matchers

The next family of built-in matchers in RSpec are known as comparison matchers. They are very straightforward and useful when comparison between a subject and expectation is required rather than equality. You will find that these matchers work very similarly to comparison operations in Ruby and other programming languages.

be Comparison Matcher

In order to implement comparison matchers, we make use of mathematical comparison operators together with the be keyword.

expect(10).to be > 9

The argument encapsulated within theexpect method should combine with what comes after be to form a boolean statement. In this case, 10 is greater than 9. Any standard mathematical comparison can be used with the be keyword.

One-liner Examples

As a side note, comparison matchers (and built-in matchers of any kind for that matter) can be condensed down with RSpec’s one-liner syntax. In this case, we can define an example and its expectation all on a single line using the is_expected method inside a block.

Notice that 10 becomes the subject for each one-line example inside its block. As long as a group’s subject has a specific value, we can write our examples in this fashion.

To Be Continued

Next time, we will continue learning about RSpec testing by diving into predicateand allmatchers, as well as other ways to use the be matcher with truthy and falsey values. With equality and comparison matchers under our belt however, we are well on our way to becoming a seasoned RSpec developer. I look forward to traversing this series together with you all!

--

--

Erik Huang
Erik Huang

Written by Erik Huang

I am currently a student in the software engineering program at Flatiron school