Skip to main content

Testing

RSpec#

We use RSpec for testing, a BDD testing framework for Ruby.

We heavily follow the RSpec best practices outlined in betterspecs.org.

Describe / Context Naming#

Use # as a prefix for instance methods and . as a prefix class method test contexts. Use describe when describing public interfaces and context for inner

describe Class do  describe '.class_method' do    it { ... }
    context 'something' do      it { ... }    end  end
  describe '#instance_method' do    it { ... }  endend

Object Creation#

In order to keep the runtime of the test suite down, it's important to create as few objects as possible. The implication is that you should use let instead of let! whenever possible. let lazily creates objects when they are needed.

When do you use a let!? If you need the object to exist in the database before the spec is run - for example:

let!(:loyalty_program) { create :program, :earn_loyalty_spend, merchant: merchant }

allows the code under test to run merchant.loyalty_program successfully.

Spec Organization#

Try to write your specs so that in each context you are changing one thing.

GOOD
Example

describe MyAwesomeClass do  let(:user) { create :user }  let(:card) { create :card, state: state, expired_at: expired_at }  let(:expired_at) { nil }
  context 'the card is linked' do    let(:state) { 'linked' }
    it 'works' do      ...    end
    context 'the card is expired' do      let(:expired_at) { Time.current }
      it 'does a thing' do        ...      end    end  end
  context 'the card is archived' do    let(:state) { 'archived' }
    it 'does not work' do      ...    end  endend

BAD
Example

describe MyAwesomeClass do  let(:user) { create :user }  let(:card) { create :card, state: state, expired_at: expired_at }  let(:expired_at) { nil }
  context 'the card is linked and expired' do    let(:state) { 'linked' }    let(:expired_at) { Time.current }
    it 'works' do      ...    end
    it 'does a thing' do      ...    end  end
  context 'the card is archived' do    let(:state) { 'archived' }
    it 'does not work' do      ...    end  endend

VCR#

When interacting with third-party APIs, we use VCR to record HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests.