Usefull Gems for testing a Ruby and Ruby on Rails app

#automated-test, #dev, #ruby

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.

Posts in this series

References