Fixturama
Collection of helpers for dealing with fixtures in RSpec
Read the post about the library on dev.to.
Installation
gem "fixturama"
Configuration
On Rails add offsets to id sequences of database tables.
# spec/rails_helper.rb
RSpec.configure do |config|
config.before(:suite) { Fixturama.start_ids_from 1_000_000 }
end
Now when you hardcode ids in fixtures (under 1_000_000), they won't conflict with authomatically created ones.
Usage
# spec/spec_helper.rb
require "fixturama/rspec"
The gem defines 3 helpers (support ERB bindings):
load_fixture(path, **opts)
to load data from a fixture, and deserialize YAML and JSONseed_fixture(path_to_yaml, **opts)
to prepare database using the FactoryBotstub_fixture(path_to_yaml, **opts)
to stub some classes
Loading
# spec/models/user/_spec.rb
RSpec.describe "GraphQL mutation 'deleteProfile'" do
subject { Schema.execute(mutation).to_h }
before do
seed_fixture("#{__dir__}/database.yml", profile_id: 42)
stub_fixture("#{__dir__}/stubs.yml", profile_id: 42)
end
let(:mutation) { load_fixture "#{__dir__}/mutation.graphql", profile_id: 42 }
let(:result) { load_fixture "#{__dir__}/result.yaml" }
it { is_expected.to eq result }
it "deletes the profile" do
expect { subject }.to change { Profile.find_by(id: 42) }.to nil
end
it "sends a notification" do
expect(Notifier)
.to (:create)
.with("profileDeleted", 42)
subject
end
end
Notice, that since the v0.0.6
the gem also supports binding any ruby object, not only strings, booleans and numbers:
# ./data.yml
---
account: <%= user %>
# Bind activerecord model
subject { load_fixture "#{__dir__}/data.yml", user: user }
let(:user) { FactoryBot.create :user }
# The same object will be returned
it { is_expected.to eq account: user }
The object must be named in the options you send to the load_fixture
, stub_fixture
, or seed_fixture
helpers.
This feature can also be useful to produce a "partially defined" fixtures with RSpec argument matchers:
subject { load_fixture "#{__dir__}/data.yml", user: kind_of(ActiveRecord::Base) }
Seeding
The seed (seed_fixture
) file should be a YAML/JSON with opinionated parameters, namely:
type
for the name of the FactoryBot factorytraits
for the factory traitsparams
for parameters of the factory
# ./database.yml
#
# This is the same as
# `create_list :profile, 1, :active, id: profile_id`
---
- type: profile
traits:
- active
params:
id: <%= profile_id %>
Use the count: 2
key to create more objects at once.
Stubbing
Another opinionated format we use for stubs (stub_fixture
). The gem supports stubbing both message chains and constants.
For message chains:
class
for stubbed classchain
for messages chainarguments
(optional) for specific argumentsactions
for an array of actions for consecutive invocations of the chain
For constants:
const
for stubbed constantvalue
for a value of the constant
Every action either return
some value, or raise
some exception
# ./stubs.yml
#
# The first invocation acts like
#
# allow(Notifier)
# .to receive_message_chain(:create)
# .with(:profileDeleted, 42)
# .and_return true
#
# then it will act like
#
# allow(Notifier)
# .to receive_message_chain(:create)
# .with(:profileDeleted, 42)
# .and_raise ActiveRecord::RecordNotFound
#
---
- class: Notifier
chain:
- create
arguments:
- :profileDeleted
- <%= profile_id %>
actions:
- return: true
- raise: ActiveRecord::RecordNotFound
- const: NOTIFIER_TIMEOUT_SEC
value: 10
mutation {
deleteProfile(
input: {
id: "<%= profile_id %>"
}
) {
success
errors {
message
fields
}
}
}
# ./result.yaml
---
data:
deleteProfile:
success: true
errors: []
With these helpers all the concrete settings can be extracted to fixtures.
I find it especially helpful when I need to check different edge cases. Instead of polluting a specification with various parameters, I create the sub-folder with "input" and "output" fixtures for every case.
Looking at the spec I can easily figure out the "structure" of expectation, while looking at fixtures I can check the concrete corner cases.
License
The gem is available as open source under the terms of the MIT License.