Skip to content

Using Fixtures in RSpec

Ngan Pham edited this page Feb 26, 2026 · 2 revisions

Using Fixtures in RSpec

Setup

# spec/rails_helper.rb
require "fixture_kit/rspec"

RSpec.configure do |config|
  config.use_transactional_fixtures = true
end

The RSpec entrypoint sets:

  • fixture_path default to spec/fixture_kit
  • adapter to FixtureKit::RSpecAdapter

Named Fixtures

Reference a fixture file by name. FixtureKit loads it from spec/fixture_kit/.

RSpec.describe "billing" do
  fixture "company/with_payroll"

  it "has a company with employees and payroll" do
    expect(fixture.payroll.company.users.count).to be >= 1
  end

  it "rolls back between examples" do
    User.create!(name: "Temporary")
    expect(User.count).to eq(User.count) # extra user exists only here
  end

  it "starts clean each example" do
    # the temporary user from above is gone
    expect(User.where(name: "Temporary")).to be_empty
  end
end

Anonymous Fixtures

Define fixture data inline when you don't need a reusable file:

RSpec.describe User do
  fixture do
    admin = User.create!(name: "Admin", email: "admin@example.com", role: "admin")
    member = User.create!(name: "Member", email: "member@example.com", role: "member")
    expose(admin: admin, member: member)
  end

  it "distinguishes roles" do
    expect(fixture.admin.role).to eq("admin")
    expect(fixture.member.role).to eq("member")
  end
end

Inherited Fixtures

Extend a named fixture to build on existing data without duplicating setup:

RSpec.describe "employee onboarding" do
  fixture(extends: "company/base") do
    onboarding = Onboarding.create!(company: parent.company, admin: parent.owner)
    expose(onboarding: onboarding)
  end

  it "associates the onboarding with the company" do
    expect(fixture.onboarding.company.name).to eq("Acme Corp")
  end

  it "uses the company owner as admin" do
    expect(fixture.onboarding.admin.role).to eq("owner")
  end
end

The parent helper gives access to the parent fixture's exposed records. Parent data is in the database but only the child's expose calls determine what's available on fixture.*.

Overriding in Nested Groups

Child example groups can declare a different fixture:

RSpec.describe Project do
  fixture "company/with_employees"

  it "has employees" do
    expect(fixture.employee).to be_present
  end

  context "with payroll" do
    fixture "company/with_payroll"

    it "has payroll data" do
      expect(fixture.pay_period).to be_present
    end
  end
end

Lifecycle Timing

For a group that declares fixture:

  1. Declaration is registered when the example group is defined.
  2. Cache generation runs in prepend_before(:context) for that group.
  3. Cache mount runs in prepend_before(:example) and assigns the repository.

Runner start (FixtureKit.runner.start) runs once in before(:suite) and clears the cache path unless preserve-cache is enabled.

Metadata Key

FixtureKit stores the declaration on group metadata key :fixture_kit_declaration.

Common Gotcha

If a spec doesn't declare fixture and still calls fixture, FixtureKit raises:

No fixture declared for this example group. Use `fixture "name"` in your describe/context block.

Clone this wiki locally