3 mistakes to avoid in your rspec tests

rspec is an amazing testing tool for Ruby and Rails apps. I have used it in most of the projects I’ve worked on for a long time. Despite this, I am still prone to make mistakes, so I’ll keep a log of some of them here for future reference

Failing to use hooks correctly

Can you spot the error in the following code?

context 'some setup code here' do
    call_some_setup_function(x, y)

    it 'validates some conditions' do
        expect(sum(1, 2)).to eq 3
    end
end

To help you along, this is the error message produced. Any luck?

NoMethodError:
  undefined method `call_some_setup_function' for #<Class:0x000040229933d0>

I don’t know about you but it took me quite some time to catch the source of the error. The mistake here is that the setup function should be called in a before block

context 'some setup code here' do
    before { call_some_setup_function(x, y) }

    it 'validates some conditions' do
        expect(sum(1, 2)).to eq 3
    end
end

Not using the full range of matchers

I wouldn’t be able to list all the possible matchers available by default in rspec here. For that, take a look at the Built-in matchers documentation

Here are some of them that I’ve found to be useful:

expect(result).to be_success      # passes if result.success?
expect(result).to be_failure      # passes if result.failure?
expect(result).to be_processing   # passes if result.processing?

# arrays
expect(actual).to include(expected)
expect(array).to match_array(expected_array) # exact array match

# strings
expect('gato').not_to include('ef')
expect('hola').to include('h')

# changes to object count in DB from some external function
# e.g. HTTP Request
expect { good_post_request }.to change { Account.count }.by(1)
expect { bad_post_request }.not_to change { Account.count }

Repeating specs

If you have a set of specs that you seem to keep repeating in many different tests, put them in a shared_examples block

shared_examples 'account creation' do
  it 'succeeds' do
    expect { post :create, data: data }.to change { Account.count }
    expect(response).to be_success
    # ... and any other expectations for the new account
  end
end

# ...and to use it in other places without repeating the examples
include_examples 'account creation'

You can also share and re-use common test setup steps with shared_context and include_context

Pro tip: Under the hood shared_examples , shared_examples_for and shared_context are all the same thing

rspec-core/lib/rspec/core/shared_example_group.rb at 8caecca0b9b299ccbaa5c7ea5dd885ab42cd57d3 · rspec/rspec-core · GitHub

I still need to understand this better and you can find more information in the shared examples documentation

Happy testing!


Cover Photo by Sarah Pflug from Burst