A Guide To RSpec Built-in Matchers Part 2
Click here for Part 1
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.
Continuing on from Part 1, we will be focusing on three built-in matchers that are based around boolean values and concepts. I particularly enjoy using this week’s matchers because, as you’ll soon see with predicate matchers, they are closely related to their vanilla Ruby counterparts and comprise some of the most useful built-in matchers featured in RSpec.
In Ruby, a predicate method is defined with a
? at the end of the method name and returns a boolean value based on the argument provided to it. Predicate methods do not need to end with a question mark, but it is considered best-practice to do so. Some of the most common predicate methods include
In RSpec, each predicate method has an accompanying matcher that can be used for testing purposes. Calling these predicate matchers within an assertion is very straightforward: simply remove the
? at the end and prepend the phrase
be_ to the start of the method name. For example:
even? -> be_even
odd? -> be_odd
zero? -> be_zero
Couldn’t be simpler, right? Naturally, every predicate method that Ruby offers can be converted to a predicate matcher in this fashion. They can also be used in conjunction with either
not_to to further alter the boolean values being returned.
Using the four methods referenced above, let’s write some examples and assertions implementing predicate matchers that expect true or false values to be returned:
Since all predicate matchers begin with the
be keyword, it can be difficult and confusing when differentiating between predicate matchers and other
be matchers such as comparisons and, as we will later see, truthy/falsey/nil
be matchers. However, these naming conventions for different kinds of matchers are insignificant.
The purpose of grouping these matchers within defined names is simply to organize the contents of this guide. As long as you clearly understand what your assertions are checking for, and what values they should return, you needn’t worry about issues such as whether your assertion uses a predicate or comparison matcher. If all else fails, the Ruby and RSpec docs never fail to procure additional information and details related to these groups of matchers.
The all Matcher
all matcher in RSpec is specifically designed to test collections/reference types in Ruby and be used together with other matchers. When passed to an assertion after the
not_to method, it checks every element within a collection and returns false unless every single element meets the expectation. Again, it should be emphasized that
all is meant to be used together with other RSpec methods such as comparison or predicate matchers.
Passing in another method to the
all matcher is just like any other argument in Ruby: you can do so inside of parenthesis or separate it with a space after
expect([1, 3, 5]).to all(be_odd)
expect([2, 4, 6]).to all be_even
Let’s try writing some examples to test an array of numbers. We want to check whether all the values inside our array are even and whether each value is within a certain range.
Through this example group, you can hopefully see just how versatile the
all matcher is and how naturally it combines with predicate, comparison, or even equality matchers. The RSpec syntax is phenomenal at clearly describing what each example is checking and what each assertion should return.
RSpec’s ability to combine different keyword methods like
be make it so that even Ruby newbies can learn almost everything they need to know about how a codebase works from the spec files. This is an extremely useful tool to add to any developer’s arsenal.
be Matchers with Truthy, Falsey, & Nil values
Inside the Ruby landscape we all know and love, all values possess an innate level of “truthiness” or “falsiness”. In other words, everything in Ruby will evaluate to be either truthy or falsey.
Although there is some consistency across most programming languages involving truthiness/falsiness, Ruby only contains two falsey values:
false. Everything else, whether it is a string, integer, float, array, hash, or 0, will always evaluate to be truthy.
RSpec also allows us to test subjects using the
be keyword together with truthy, falsey, or nil assertions. Remember,
nil is a special Ruby object that represents an absence of value. Therefore, anything that computes to
nil will inherently be “falsey” AND “nil”.
In order to use
be matchers with truthy/falsey/nil values, simply append the appropriate word to
be using an underscore. As earlier stated, these methods are very similar to how other predicate matchers are used in RSpec.
expect("Daisaku Kuze").to be_truthy
Since any value besides
nil are “falsey”, these matchers can be a great way to check for the existence of certain objects or variables within your code. Lastly, let’s practice by writing out a bunch of assertions in RSpec that check for truthy/falsey values:
There are many assertions provided in this particular Gist, so please take your time when reading exactly what each line does. The secret to mastering this week’s RSpec’s matchers is having a clear understanding of truthy and falsey values, and developing the practical skills to utilize them to maximum effect in your unit tests.
To Be Continued
We have now covered equality, comparison, predicate, and
all/be matchers in RSpec, how they are used in assertions, and how they can be combined with each other. In addition to deepening our understanding of testing in Ruby, having a good command of the matchers in this blog will help you develop ways to write a wide variety of examples in your spec files. Next week, we will touch on even more built-in matchers and take things to the next level with Ruby code blocks!