CtrlK
BlogDocsLog inGet started
Tessl Logo

igmarin/rails-agent-skills

Curated library of 41 public AI agent skills for Ruby on Rails development. Organized by category: planning, testing, code-quality, ddd, engines, infrastructure, api, patterns, context, and orchestration. Covers code review, architecture, security, testing (RSpec), engines, service objects, DDD patterns, and TDD automation. Repository workflows remain documented in GitHub but are intentionally excluded from the Tessl tile.

95

1.77x
Quality

93%

Does it follow best practices?

Impact

96%

1.77x

Average score across 41 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

SKILL.mdskills/testing/write-tests/

name:
write-tests
license:
MIT
description:
Use when writing, reviewing, or cleaning up RSpec tests for Ruby and Rails codebases. Covers spec type selection, factory design, flaky test fixes, shared examples, deterministic assertions, test-driven development discipline, and choosing the best first failing spec for Rails changes. Also applies when choosing between model, request, system, and job specs. Trigger words: write spec, rspec, test-driven development, testing.
metadata:
{"version":"1.0.0","user-invocable":"true"}

Write Tests

Use this skill when the task is to write, review, or clean up RSpec tests.

Core principle: Prefer behavioral confidence over implementation coupling. Good specs are readable, deterministic, and cheap to maintain.

Quick Reference

AspectRule
Spec typesModel: domain logic; Request: HTTP endpoints (prefer over controller); Job: background processing; Service/PORO: no Rails helpers; System: critical E2E only (slow)
AssertionsTest behavior, not implementation
FactoriesMinimal — only attributes needed; use traits for optional states; prefer build/build_stubbed over create
MockingStub external boundaries, not internal code
IsolationEach example independent; no shared mutable state
Namingdescribe for class/method, context for scenario
Service specsRequired: describe '.call' and subject(:result) for the primary invocation
let vs let!Default to let; let! ONLY when object must exist before example runs
External service mockingallow(ServiceClass).to receive(:method)not instance_double; instance_double only for injected collaborators
Example namesPresent tense: it 'returns the user'; NEVER it 'should ...'; NEVER contains and — see One Behavior Per Example
aggregate_failuresUse when asserting multiple related items in one example

HARD-GATE

TESTS GATE IMPLEMENTATION:
DO NOT write implementation code before a failing test exists.
When writing tests for new behavior, follow the TDD workflow exactly:
1. Write spec
2. Run spec and verify it fails
3. Implement minimal code
4. Run spec and verify it passes

ONE BEHAVIOR PER EXAMPLE:
The word "and" in an `it` / `specify` description signals two behaviors in one example. Split it every time — no exceptions.

Core Process

When driving new behaviour with RSpec, follow this sequence:

  1. Write the failing spec — pick the smallest spec type that exercises the intended behaviour (model > service > request > system). See table below for guidance.
  2. Run it and confirm the failure message — show the concrete RED failure class/message for the first spec. Do not leave this as a placeholder template and do not use illustrative e.g. failure examples in the final artifact.
  3. Implement the minimum code to make the spec pass.
  4. Refactor — clean up duplication and naming while keeping the suite green.
  5. Verify — run the full relevant spec file, then the suite, before committing.

Choosing the best first failing spec for a Rails change

Change typeStart with
Pure domain logicModel or PORO service spec
HTTP endpoint behaviourRequest spec
Background processingJob spec
Cross-layer user journeySystem spec (sparingly)

Service Spec (anchor pattern)

RSpec.describe Invoices::MarkOverdue do
  describe '.call' do
    subject(:result) { described_class.call(invoice: invoice) }

    context 'when the invoice is overdue and unpaid' do
      let(:invoice) { create(:invoice, due_date: 2.days.ago, paid_at: nil) }
      it 'marks the invoice overdue' do
        expect { result }.to change { invoice.reload.overdue? }.from(false).to(true)
      end
    end
  end
end

One Behavior Per Example

# BAD — two assertions; if the first fails, the second never runs
it 'returns 201 and creates the record' do; end

# GOOD — one observable outcome per example
it 'returns 201' do; end
it 'creates the record' do; end

Flaky Tests & Deterministic Assertions

CauseFix
Time-dependent logicfreeze_time / travel_to; never set past dates as shortcut
State leakageEach example sets up own state; avoid before(:all)
Async jobsqueue_adapter = :test + have_enqueued_job; never assert side-effects imperatively
External HTTPWebMock / VCR; never allow real network in CI
DB state bleedTransactional fixtures or DatabaseCleaner; never share let! across contexts
Race conditionsExplicit Capybara waits; avoid sleep
Imprecise assertionschange.from().to() over final state; exact values over be_truthy/be_falsey; see rule 16 for updated_at

Extended Resources (Progressive Disclosure)

Load these files only when their specific content is needed:

  • EXAMPLES.md — For code examples of service specs, shared examples, and factory design.
  • assets/spec_templates.md — Standard templates for different types of specs.
  • assets/tdd_proof_checklist.md — Use when the task involves new behavior and the final answer must show RED/GREEN proof.

Output Style

When asked to write or review RSpec specs, your output MUST satisfy each rule below. Each is graded independently — one violation drops the whole check.

  1. Spec file path mirrors the source: app/foo/bar.rbspec/foo/bar_spec.rb.
  2. # frozen_string_literal: true as the first line of every spec file.
  3. RSpec.describe uses the full constant path (RSpec.describe Module::Class do), not a string.
  4. describe '#method' / describe '.class_method' for each method under test.
  5. context 'when ...' / context 'with ...' for scenario variations — never use context to group methods.
  6. let for test data, let! ONLY when the object must exist before the action under test.
  7. No let_it_be unless the project already depends on test-prof (check Gemfile.lock first).
  8. NO "and" in any example description — Split them. Perform an explicit scan before returning the spec.
  9. subject(:result) { ... } for service / PORO specs invoking .call.
  10. travel_to / freeze_time for any time-dependent assertion — never set past Time.now or stub Time.current directly.
  11. External boundaries mocked at the class-method level (allow(SomeClient).to receive(:method)); ActiveRecord finders are NEVER mocked.
  12. TDD failure proof — State the smallest spec type chosen, the command run, and the concrete observed failing message proving missing behavior rather than broken setup. Do not return only a RED proof template with placeholders, and do not write e.g. before the failure message.
  13. Verification proof — After implementation, state the passing focused rerun, the full relevant spec file, and the broader suite command when available.
  14. Minimal factories — Use only explicit attributes needed for the behavior; prefer traits for optional states and build / build_stubbed unless persistence is required. Do not hide business-meaningful defaults in the factory.
  15. Multiple related assertions — Use aggregate_failures when one behavior needs several related expectations, and show it in the produced spec when relevant.
  16. Timestamp assertions — Never assert updated_at unless time is frozen and the timestamp change is the behavior under test.
  17. Self-audit — Before returning, include a short checklist confirming:
  • No it/specify descriptions contain "and".
  • Every let! is justified by a must-exist-before-action constraint.
  • Referenced shared examples are actually included.
  • Shared examples are avoided when each context needs different setup.
  • Factories use the least-persistent setup that proves the behavior.
  • Time-dependent records are created before travel_to when the original timestamp matters.
  1. Language — Must be in English unless explicitly requested otherwise.

Integration

SkillWhen to chain
plan-testsChoosing the best first failing spec for a Rails change
create-service-objectProviding test structure for the .call pattern
refactor-codeAdding characterization tests before refactoring
implement-graphqlWriting specs for GraphQL resolvers and mutations

skills

README.md

server.json

tile.json