Usefull Gems for testing a Ruby and Ruby on Rails app
I’ve picked my most recently project I worked and looked up what gems I was using. This is the first list if I ever find another project different I will create another list (or update this one).
Except for the first one there is no important order.
RSpec
RSpec is the base tool we are using in this app. It is a Behavior-driven development gem.
Its main point is to test the behavior (or features) of our app instead of testing just every piece of code.
Database Cleaner
https://github.com/DatabaseCleaner/database_cleaner
It is used to clear the database in every test (remember: each it is one very test itself).
There is only a setup of this gem, e. g., we don’t write code to it in the tests.
factory_bot
https://github.com/thoughtbot/factory_bot
In the tests, it is used to reduce duplication of code.
For example, instead of having this:
let(:user) do
User.create!(name: "Test User", email: "test@example.com", password: "password", group: Group.create!(name: "test"))
end
let(:meeting) do
Meeting.create!(name: "Meeting#1", body: "Body for Meeting#1", source: "Source for Meeting#1", status: MeetingStatus::ACTIVE, user: user)
end
It is possible to write only:
let(:meeting) { create(:meeting) }
Everything else you write only once in spec/factories, for the example above you can create three files:
spec/factories/users.rb:
FactoryGirl.define do
factory :user do
name "Test User"
email "test@example.com"
password "password"
confirmed_at { Time.current }
association :group
end
end
spec/factories/groups.rb:
FactoryGirl.define do
factory :user do
name "Group#1"
end
end
And spec/factories/meetings.rb:
FactoryGirl.define do
factory :meeting do
name "Meeting#1"
body "Body for Meeting#1"
source "Source for Meeting#1"
status MeetingStatus::ACTIVE
association :user
end
end
And whenever you need a new object you just call create(:meeting) or build(:meeting).
It basically improves readability of code, speeds up writing, and helps you to keep them updated.
Shoulda Matchers
https://github.com/thoughtbot/shoulda-matchers
Should matchers is a collection of one-line code to simplify test code writing. Usually it replaces longer, complex, and error-prone test code.
For example, instead of writing something like this:
RSpec.describe Person, type: :model do
subject { Person.new(name: "Alice") }
it "is expected to be valid" do
expect(subject).to be_valid
end
it "is expected to validate presence of name" do
subject.name = nil
expect(subject).not_to be_valid
expect(subject.errors[:name].size).to eq(1)
end
end
It is possible to write only:
RSpec.describe Person, type: :model do
subject { Person.new(name: "Alice") }
it { is_expected.to be_valid }
it { is_expected.to validate_presence_of(:name) }
end
Shoulda matchers is one of most controversial gems for me, I don’t use it for personal projects, but I do use it for big ones just because it creates consistence through code base, without consistence and with a lot of developers everyone will write the way they like.
SimpleCov
https://github.com/colszowka/simplecov
It is a tool used to verify the test coverage of Ruby code. It uses Ruby’s built-in coverage library to verify which line of code is running.
It is not a fully reliable gem because of the way Ruby’s built-in coverage library check if a line is called and it might result in false-positive.
For example:
File code.rb:
def is_ok?(value)
if value.is_a?(String) || value.is_a?(Float)
return true
end
end
File coverage.rb
require "coverage.so"
Coverage.start
require "./code"
puts is_ok?("String") == true
puts Coverage.result
Results:
$ ruby ./coverage.rb
true
{"/home/dmitry/Dev/qotient/ruby-s-coverage/code.rb"=>[1, 1, 1, nil, nil]}
Analysing the results we have 100% of “coverage” but actually, we are not testing this code: || value.is_a?(Float), and, a human can say it is only 50% tested.
But it is an extraordinary gem to receive feedback and put us back on course for a 100% test coverage.
This is other controversial gem to me, I like seeing a project 90% up to 99% tested, but I always remind myself that it is not that true. And if you are adding test to an app you should be awere of Parento principle. 100% of coverage is not always true.
timecop
https://github.com/travisjeffery/timecop
It is a gem to freeze and travel in time while writing tests. Sometimes when writing test code it is necessary to wait for something.
For example, let’s say that if I schedule today a meeting for next week and I want to receive a notification one hour before, using pseudo-code we can write something like this:
create(:meeting, date: Date.NextWeek, notification: (Date.NextWeek - 1.hour))
Timecop.travel_to(Date.NextWeek - 1.day)
expect(Notification.count).to eq 0
Timecop.travel_to(Date.NextWeek + 20.minutes)
expect(Notification.count).to eq 1
VCR and webmock
https://github.com/vcr/vcr and https://github.com/bblimke/webmock
It is a tool to “cache” external calls.
For example, we can create a test to go to Google and search by a name and test if our website is on the first page. But what happens when we don’t have an internet connection? Or if there is an issue in our DNS? Or Google is down?
We can solve those problems and a lot of others by caching the request once and using that cache always when we need. And if we are not sure whether the result was changed we can just remove that cache and retrieve it again from the original source.