A Guide To RSpec Built-in Matchers Part 1

Equality & Comparison Matchers

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 and 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 followed by a call to the method.

expect(number).to eq(9)

This creates a basic assertion expecting a variable called to have a value of . The 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 is not equal to the string . I find that a great way to practice writing tests is to always try and construct a method assertion that exactly mirrors what another 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 after / 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 matcher, that checks for value equality between a subject and its expectation. Using , we can expect that is to , the length of the string is to the length of the string , or that is to .

Each of these tests pass using the eq matcher

works very similarly to , but it checks for value AND type equality. In other words, an integer cannot be 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, matchers provide an extra bit of security that your business logic is working the way it should be. in contrast, provides you with the flexibility of allowing type conversions (.

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 and 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 keyword. What happens if we make an assertion using to compare to ?

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 highlights the exact reason our test failed. Even though they contain the exact same key/value pairs, is not to because their pointers are referencing two completely different hashes in memory. This can clearly be seen in the lines as well.

In order to allow our test to pass, we need to add in a third variable using that points to the same position in memory as one of the other variables, and use or 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 can be replaced with the keyword instead. Later we will see ways in which is used together with other types of matchers as well.

Remember these guidelines when using equality matchers:

  • checks for value equality only
  • checks for value and data type equality
  • 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 keyword.

expect(10).to be > 9

The argument encapsulated within the method should combine with what comes after to form a boolean statement. In this case, 10 is greater than 9. Any standard mathematical comparison can be used with the 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 method inside a block.

Notice that 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 and matchers, as well as other ways to use the 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!

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