Using RSpec’s change Matcher to Test Record Creation

Daniel Pericich
5 min readMay 21, 2021

--

Photo by CardMapr.nl on Unsplash

Intro to E-commerce

If you work with e-commerce for long enough, you will likely run into the issue of conditional money transaction records. While services like Stripe make it very easy to handle credit card transactions, these don’t account for all internally managed payments such as company gift cards and promotional funds. Customers love these different payment options, but as a developer these can add migraine inducing complexity to your project.

With new forms of payment, your linear checkout process will soon branch into a complex decision chart with credit card transactions, promotional fund transactions, gift card balances and every other combination that you can think of. While you may have been easily recording every single credit card transaction before, now you will have a larger number of different transaction records to track.

Not every one of these uses will apply so you will need to write logic and matching tests to make sure that you don’t miss out on your transaction records. Luckily for us, RSpec has the change matcher which is great for testing that a certain record was created for specific workflows.

What are RSpec matchers?

Before we dive into the change matcher, let’s first talk about what matchers are. In RSpec there are two parts of a test expression. These parts are an expect method and a matcher. The expect method accepts the object or value you are testing on, while the matcher takes a value or reaction that you define for what you expect the test object’s value to be in a certain state. The following is a simple example of a test expression:

Figure 1. A basic test expression to test an object’s attribute

Here we have an object ‘cat’ that we want to test. In this instance of cat we expect the name attribute to have a value of ‘Dave’. We expect the cat’s name to be Dave. And if we run this test it is Dave!

While `be` is a very common matcher for testing an object’s properties against string literals or numbers, matchers come in many different forms. Some other common types are matchers for comparisons, matchers for truthiness or existence, matchers for collection membership and matchers for expecting and handling errors. See the attached link to go even deeper on RSpec’s great matchers (https://relishapp.com/rspec/rspec-expectations/v/3-10/docs/built-in-matchers).

How we check that a record has been created

We’ve talked about the most basic matcher, and some of the various types of matchers RSpec offers, but we still need to work with the change matcher. The change matcher focuses on checking whether a block of code changes a mutable state. What this means for our e-commerce payment services is change will allow us to check if the record creation part of our gift card payment process is working correctly.

For our change example we are going to pretend that your Product Manager just came to the development team with a great idea to add gift cards to your e-commerce site. You think back to the gift card you lost 8 years ago with $7.23 left and wonder if gift cards are the best idea. Your Product Manager assures you that with online gift cards things will be different, people won’t lose their money because they have you to keep track of all their balances.

Knowing that other people won’t shrug off losing $7.23 of their aunt’s hard earned money, you decide you want to create a record every time someone uses a gift card at checkout. This record will contain info about the user placing the order, the amount paid with gift card funds and the remaining balance after checkout. With these gift_card_funds_usage records you’ll be sure no money will disappear.

To test your new checkout process you decide to write a test to make sure that a record is written every time a gift card is used during checkout. To create the record you have a gc_funds_usage method, which is part of the Order model, that creates a new gift_card_funds_usage record.

Because we are interested in the functionality of this gc_funds_usage method, it will be the test object and will be placed inside the expect method. Unlike other test expressions, change expressions use “{}” around both their test object and their matcher value.

Now that we have decided on our test object, we next need to determine what we want to check against. Because we are looking for our method to create a new record we can look at the state of our record model GiftCardFundsUsage. We are interested in how many of these records there are so we will use count to access this value.

The last thing we need to add to our expression is a method to describe how we expect the method to change the state of our model, GiftCardFundsUsage. For this we can use the change compliment “by” method. With this our test is complete:

Figure 2. Using the change assertion to check that a record was created

If we were to run this test from our terminal we would see that we have a new passing test. To stay true to red green testing, try replacing 1 with 2 and test that we aren’t getting false positives. With 2 it should

With that you have found a great way to test that one of your checkout workflows is correctly creating records. As far as you are concerned the angry customers outside your office aren’t there for you and their gift cards. Now it’s time to work on the bug that has been sending holiday cards to customers’ smart fridges…

Notes

https://stackoverflow.com/questions/30158710/how-to-write-a-spec-to-check-if-a-record-is-saved-in-the-database-using-rspec-wi

https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/change-matcher

https://www.tutorialspoint.com/rspec/rspec_matchers.htm

--

--