model-test
Skill2 starsChanged 35 days agoMITView on GitHub
Write or review the RSpec spec for one ActiveRecord model (or model concern) so it tests the model's real business behavior (custom validations, scopes, callbacks, state changes, domain methods) and drops declaration-mirroring fat. Use when the user asks for model tests, points at a file in app/models or spec/models, or wants a model spec cleaned up, trimmed, or checked for whether it catches bugs.
In this repository75 other files it gives its agents.
- acceptance-criteriaSkill
- architect-guideSkill
- architect-reviewSkill
- audit-fixerSkill
- avo-admin-expertSkill
- backend-services-expertSkill
- bug-hunt-allSkill
- bug-hunt-fixSkill
- bug-huntSkill
- build-feature-remoteSkill
- build-featureSkill
- codeSkill
- component-testSkill
- controller-expertSkill
- create-qa-documentSkill
- debugSkill
- devops-expertSkill
- execute-qaSkill
- feature-qaSkill
- fix-bug-indexSkill
- fix-honeybadgerSkill
- fix-spec-bugsSkill
- fix-system-testSkill
- frontend-expertSkill
- frontend-reviewSkill
- full-qa-fixSkill
- full-qaSkill
- full-reviewSkill
- green-ciSkill
- group-issuesSkill
- honeybadger-auditSkill
- ideateSkill
- impact-assessmentSkill
- improveSkill
- merge-mainSkill
- model-expertSkill
- native-bug-hunt-fixSkill
- native-bug-huntSkill
- page-speedSkill
- plan-questionsSkill
- planSkill
- plan-system-testsSkill
- policy-testSkill
- prdSkill
- prd-worktreeSkill
- product-questionSkill
- product-question-tldrSkill
- qa-branchSkill
- qa-testerSkill
- refactor-sweepSkill
- resolve-honeybadgerSkill
- resolve-issueSkill
- resolve-issuesSkill
- review-acceptance-criteriaSkill
- review-fixesSkill
- review-railsSkill
- rspec-test-expertSkill
- scale-reviewSkill
- service-testSkill
- settle-pr-reviewSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 1 is distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 9
Write or review the spec for the model at $ARGUMENTS.
- Distinctive: line 20
Read the model and list these.
- Very specific: line 87
Follow test-quality.md: the spec is green, coverage --baseline shows no lost_lines, and 2–5 probes are aimed at custom validations, scope …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Model Test
Write or review the spec for the model at $ARGUMENTS. First read
.claude/skills/rspec-test-expert/test-quality.md: the fat catalogue, the proof (coverage plus
probes), concurrency-safe spec runs, and the review workflow all live there. This file covers what
is particular to models.
Map paths: app/models/foo/bar.rb ↔ spec/models/foo/bar_spec.rb;
app/models/concerns/x.rb ↔ spec/models/concerns/x_spec.rb. If the spec exists, you are in
review mode (the workflow in test-quality.md); otherwise write mode.
What a model's behavior is
Read the model and list these. They are what the spec must guard:
| In the model | Test it by |
Custom validate :method, conditional validations (if:/unless:), cross-field rules | The valid and invalid case for each rule, asserting the specific errors[:field] message. Conditional: both sides of the condition. |
| Uniqueness with a scope, numeric bounds, format rules | A shoulda one-liner is fine here; it is short and asserts the rule's parameters. |
| Scopes and class query methods | Records that should and should not match, in one example. For ordering scopes, assert eq([...]) order. Include the edge that the where clause draws (date boundary, status set). |
Callbacks (before_save, after_commit, normalizers) | The observable effect: normalized value, enqueued job with args, created record. For after_commit, data must actually commit (default transactional tests do; check have_enqueued_job). |
| Enums | One example asserting the persisted integer values when they are stored or exported (define_enum_for(...).with_values(...)). Not the generated ?/! methods; that's Rails. |
State transitions and domain methods (finalize!, billable_amount, display_name) | Each branch, with exact values. Bang methods: the success path changes state; the guarded path raises or returns false and changes nothing. |
soft_delete_cascades_to, SoftDeletable | it_behaves_like "a soft-deletable model" (spec/support/shared_examples/soft_deletable_model.rb) plus one example that the declared cascade targets get soft-deleted. The concern itself is tested in spec/models/concerns/. |
| Associations | Only those whose options carry behavior: dependent: (but remember that soft-delete doesn't cascade through dependent:), optional: true where nil is a real state, a non-obvious class_name/foreign_key/inverse_of, ordered has_many. Plain belong_to(:organization) is a mirror: drop it. |
DB constraints the model relies on (check constraint, unique index, null: false, foreign key) | One example that the DB rejects the bad row (save(validate: false) / update_column → NotNullViolation/RecordNotUnique/InvalidForeignKey/StatementInvalid) when no validation covers it, or when the constraint is new on this branch, so the migration is proven to hold. |
Model-specific fat to remove
- One-liners for every association and every presence validation, copied from the class body.
- "Factory creates a valid record", trait loops,
build vs createdemos. - Re-testing concern behavior (HipaaLoggable, SoftDeletable, Normalizes…) in every includer. Test
the concern once in
spec/models/concerns/; in the includer, test only its configuration. respond_to(:soft_delete),included_modules,column_nameschecks.- N+1 or "query count" tests on plain associations. Keep a query-count example only when the model has a method whose point is preloading.
create-heavy setup wherebuildvalidates fine. Only scopes, callbacks, and uniqueness need persisted rows.
Shape
# frozen_string_literal: true
require "rails_helper"
RSpec.describe InvoiceAdjustment, type: :model do
describe "validations" do
it { is_expected.to validate_numericality_of(:amount_cents).other_than(0) }
it "rejects a write-off larger than the invoice balance" do
invoice = create(:invoice, balance_cents: 5_000)
adjustment = build(:invoice_adjustment, invoice:, kind: :write_off, amount_cents: -6_000)
expect(adjustment).not_to be_valid
expect(adjustment.errors[:amount_cents]).to include("cannot exceed the remaining balance")
end
end
describe ".posted_between" do
it "includes both boundary days and excludes the day after", :aggregate_failures do
travel_to Time.zone.local(2026, 9, 1, 12) do
first = create(:invoice_adjustment, posted_on: Date.new(2026, 8, 1))
last = create(:invoice_adjustment, posted_on: Date.new(2026, 8, 31))
after = create(:invoice_adjustment, posted_on: Date.new(2026, 9, 1))
result = described_class.posted_between(Date.new(2026, 8, 1), Date.new(2026, 8, 31))
expect(result).to contain_exactly(first, last)
expect(result).not_to include(after)
end
end
end
end(Illustrative: check the real model for names, messages, and columns. Never copy expectations from this example.)
Prove it, then report
Follow test-quality.md: the spec is green, coverage --baseline shows no lost_lines, and 2–5
probes are aimed at custom validations, scope conditions, callback effects, and transition guards,
all KILLED. Then lint with bin/standardrb <spec> and report as test-quality.md describes (or in
the caller's format).
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

