Gems úteis para testar projetos Ruby e Ruby on Rails

#automated-test, #dev, #ruby

Eu peguei um dos meus últimos projetos que trabalhei e peguei algumas gems que uso para escrever testes automatizados. Esta é a primeira lista, se eu encontrar outro projeto usando diferente gems eu atualizo esta lista ou crio outra.

Com exceção do primeiro item nenhum outro está em ordem.

RSpec

RSpec é a base para testar apps. Essa gem usa o Behavior-driven development. Ou seja, o ponto principal dela é testar o comportamento do seu app, não testar um trecho de código apenas por testar.

Database Cleaner

https://github.com/DatabaseCleaner/database_cleaner

Esta gem é usada para limpar o teste a cada execução dele (lembre-se cada it é uma execução isolada do seu teste).

Esta gem só tem setup, uma vez feito você não vai notar o uso dela.

factory_bot

https://github.com/thoughtbot/factory_bot

É usado para reduzir a duplicação do código.

Por exemplo, ao invés de ter:

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

É possível escrever somente:

let(:meeting) { create(:meeting) }

Todo o resto você escreve nos arquivos em spec/factories, para o exemplo acima você teria que escrever três arquivos:

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

E 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

E quando quiser um novo objeto apenas escreva create(:meeting) or build(:meeting).

Basicamente melhora a leitura do código, aumenta a escriva e ajuda a mantê-los atualizados.

Shoulda Matchers

https://github.com/thoughtbot/shoulda-matchers

Should matchers, como ele mesmo se descreve, é uma coleção de códigos de uma linha. Simplificando a escrita de código. Normalmente ele substitui códigos longos, complexos e sucetíveis a erro.

Por exemplo, ao invés de escrever algo como:

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

É possível que você escreva algo como:

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 é uma das gems mais controvésias para mim, eu não uso para meus projetos pequenos, mas eu uso para projetos grandes e com uma grande quantidade de programadores, apenas para criar consistência através do código, sem consistência cada programador pode acabar escrevendo do jeito que eles gostam.

SimpleCov

https://github.com/colszowka/simplecov

Muito útil para saber a cobertura de teste do seu código Ruby. Essa gem usa Ruby’s built-in coverage library, aka, um recurso interno do Ruby para saber execução de código.

O Ruby’s built-in coverage library não é muito confiável. Ele apenas verifica se aquela linha foi executada e isso pode resultar falto-positivos.

Por exemplo:

Arquivo code.rb:

def is_ok?(value)
  if value.is_a?(String) || value.is_a?(Float)
    return true
  end
end

Arquivo coverage.rb

require "coverage.so"
Coverage.start
require "./code"
puts is_ok?("String") == true
puts Coverage.result

Resultado:

$ ruby ./coverage.rb
true
{"/home/dmitry/Dev/qotient/ruby-s-coverage/code.rb"=>[1, 1, 1, nil, nil]}

Analisando o resultado percebe-se que existem 100% de covertura quando na verdade nós não testamos esse código || value.is_a?(Float), certamente uma pessos diria que está apenas 50% testado.

Mas muitas vezes é ótimo ver o resultado dessa gem para você ter um rumo para seus planos de covertura de teste na sua app.

Esta gem é uma outra controversa para mim, eu gosto de ver um projeto entre 90% e 99% testado, mas eu sempre lembro a mim mesmo que isso pode não ser verdade. E se você está adicionando teste numa app você deveria estar ciente do princípio de Pareto. 100% de covertura nem sempre é uma boa pedida.

timecop

https://github.com/travisjeffery/timecop

Gem para congelar e viajar no tempo. Algumas vezes quando se escreve teste é necessário que esperemos por algo.

Por exemplo, se eu, como usuario, quero criar um agendamento hoje para daqui a uma semana com uma notificação de uma hora antes, eu poderia um teste em pseudo código parecido com isso:

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 e https://github.com/bblimke/webmock

Esta é uma ferramenta que uso para fazer cache de requisições externas.

Por exemplo, pode-se criar um teste para ir no Google, procurar por um determinado nome e verificar se determinado conteúdo está na página. Porém o Google é um recurso externo a sua app… E se você estiver offline? Ou você tem um problema com o seu DNS? Ou até mesmo e se o Google ficar offline?

Podemos resolver esse e vários outros problemas apenas cacheando a requisição uma vez e usando o cache sempre que precisarmos. E se você não tiver seguro que se o resultado mudou basta apagar o cache e rodar seu teste novamente.

Posts nesta série

Referências