write-rom-spec
Use this skill when writing specs for ROM Relations and Repositories in Hanami 2.x.
Workflow
Follow these steps in order, treating each as a checkpoint:
Step 0 — Configure transactional rollback (prerequisite)
Before writing any spec, ensure every test is wrapped in a transaction that rolls back. Place this in a shared context (e.g., spec/support/db_rollback.rb) and include it in your repo/relation specs:
# spec/support/db_rollback.rb
RSpec.shared_context "db rollback" do
around do |example|
rom = Hanami.app["persistence.rom"]
rom.gateways[:default].connection.transaction(rollback: :always, auto_savepoint: true) do
example.run
end
end
end
Then include it via metadata in spec/spec_helper.rb:
RSpec.configure do |config|
config.include_context "db rollback", type: :repo
config.include_context "db rollback", type: :relation
end
Step 1 — Create the spec file
Place it under spec/relations/ or spec/repos/. Initialize relation or repo in a let block and define all test data inline:
# spec/repos/users_spec.rb
RSpec.describe MyApp::Repos::UserRepo, type: :repo do
let(:repo) { described_class.new }
it "creates and finds a user by email" do
user = repo.create(email: "alice@example.com", name: "Alice")
expect(user.id).not_to be_nil
found = repo.find_by_email("alice@example.com")
expect(found.name).to eq("Alice")
end
it "updates a user" do
user = repo.create(email: "alice@example.com", first_name: "Alice")
repo.update(user.id, first_name: "Alicia")
expect(repo.by_id(user.id).one.first_name).to eq("Alicia")
end
it "raises when user is not found" do
expect { repo.by_id(99999).one! }.to raise_error(ROM::TupleCountMismatchError)
end
end
# spec/relations/users_spec.rb
RSpec.describe MyApp::Relations::Users, type: :relation do
let(:relation) { Hanami.app["persistence.rom"].relations[:users] }
let!(:alice) { relation.insert(email: "alice@example.com", active: true) }
let!(:bob) { relation.insert(email: "bob@example.com", active: false) }
it "returns active users" do
active_users = relation.active.to_a
expect(active_users.length).to eq(1)
expect(active_users.first[:email]).to eq("alice@example.com")
end
end
Step 2 — Run the test and implement
Confirm the spec fails, then write only the minimal ROM query or persistence logic to make it pass:
bundle exec rspec spec/repos/users_spec.rb # expect failure
# implement the relation/repo method …
bundle exec rspec spec/repos/users_spec.rb # expect green
Common Mistakes
- Skipping Database Transactions: Failing to wrap specs in a transactional rollback pollutes test databases and creates flaky/order-dependent tests.
- Persistent State Pollution: Using
before(:all)for database data setup, which operates outside individual test transaction boundaries. - Direct Entity Instantiation: Manually instantiating ROM Structs instead of writing to the DB via repository/changeset calls to test lookup.
Integration
| Related Skill | When to chain |
|---|---|
| create-repository | Repository creation precedes spec implementation. |
| define-relation | Relation definitions are verified via custom specs. |