Skip to content
Back to skills

Rspec Service Testing

ASecurity

Use when writing RSpec tests for service objects, API clients, orchestrators, or business logic in spec/services/. Covers instance_double, FactoryBot hash factories, shared_examples, subject/let blocks, context/describe structure, aggregate_failures, change matchers, travel_to, and error scenario testing.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 2, 2026
ai-agentsrubyrailstestingrefactoringapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned September 2, 2026

npx -y skills add FJRG2007/enigma --skill rspec-service-testing --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Rspec Service Testing?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Rspec Service Testing
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/fjrg2007-rspec-service-testing/badge)](https://www.skillsdirectory.com/skills/fjrg2007-rspec-service-testing)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: rspec-service-testing
license: MIT
description: >
  Use when writing RSpec tests for service objects, API clients, orchestrators, or
  business logic in spec/services/. Covers instance_double, FactoryBot hash factories,
  shared_examples, subject/let blocks, context/describe structure, aggregate_failures,
  change matchers, travel_to, and error scenario testing.
---

# RSpec Service Testing

Use this skill when writing tests for service classes under `spec/services/`.

**Core principle:** Test the public contract (`.call`, `.find`, `.search`), not internal implementation. Use `instance_double` for isolation, `create` for integration.

## Workflow: Write → Run → Verify → Fix

```text
1. WRITE:   Write the spec (happy path + error cases + edge cases)
2. RUN:     bundle exec rspec spec/services/your_service_spec.rb
3. VERIFY:  Confirm failures are for the right reason (not a typo or missing factory)
4. FIX:     Implement or fix until the spec passes
5. SUITE:   bundle exec rspec spec/services/ — verify no regressions
```

**DO NOT implement the service before step 1 is written and failing for the right reason.**

## Quick Reference

| Aspect | Rule |
|--------|------|
| File location | `spec/services/module_name/service_spec.rb` |
| Subject | `subject(:service_call) { described_class.call(params) }` |
| Unit isolation | `instance_double` for collaborators |
| Integration | `create` for DB-backed tests |
| Multi-assertion | `aggregate_failures` |
| State verification | `change` matchers |
| Time-dependent | `travel_to` |
| API responses | FactoryBot hash factories (`class: Hash`) |

## Spec Template

```ruby
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe ModuleName::MainService do
  describe '.call' do
    subject(:service_call) { described_class.call(params) }

    let(:shelter) { create(:shelter, :with_animals) }
    let(:params) do
      { shelter: { shelter_id: shelter.id }, items: %w[TAG001 TAG002] }
    end

    context 'when input is valid' do
      before { create(:animal, tag_number: 'TAG001', shelter:) }

      it 'returns success' do
        expect(service_call[:success]).to be true
      end
    end

    context 'when shelter is not found' do
      let(:params) { super().merge(shelter: { shelter_id: 999_999 }) }

      it 'returns error response' do
        expect(service_call[:success]).to be false
      end
    end

    context 'when input is blank' do
      let(:params) { { shelter: { shelter_id: nil }, items: [] } }

      it 'returns error response with meaningful message' do
        aggregate_failures do
          expect(service_call[:success]).to be false
          expect(service_call[:errors]).not_to be_empty
        end
      end
    end
  end
end
```

Use `instance_double` for unit isolation:

```ruby
let(:client) { instance_double(Api::Client) }
before { allow(client).to receive(:execute_query).and_return(api_response) }
```

Use `create` for integration tests:

```ruby
let(:source_shelter) { create(:shelter, :with_animals) }
```

## FactoryBot Hash Factories for API Responses

When testing API clients, use `class: Hash` with `initialize_with` to build hash-shaped response fixtures. A minimal example:

```ruby
FactoryBot.define do
  factory :api_animal_response, class: Hash do
    tag_number { 'TAG001' }
    status     { 'active' }

    initialize_with { attributes.stringify_keys }
  end
end

# In the spec:
let(:api_response) { build(:api_animal_response, tag_number: 'TAG002') }
```

See [PATTERNS.md](./PATTERNS.md) for the full pattern and factory placement guidance.

## New Test File Checklist

- [ ] `subject` defined for the main action
- [ ] `instance_double` for unit / `create` for integration
- [ ] Happy path for each public method
- [ ] Error and edge cases (blank input, invalid refs, failures)
- [ ] Partial success scenarios where relevant
- [ ] `shared_examples` for repeated patterns
- [ ] `aggregate_failures` for multi-assertion tests
- [ ] `change` matchers for state verification
- [ ] Logger expectations for error logging

## Common Mistakes

| Mistake | Correct approach |
|---------|------------------|
| No error scenario tests | Happy path only = false confidence — always test failures |
| `let!` everywhere | Use `let` (lazy) unless the value is needed unconditionally for setup |
| Huge factory setup | Keep factories minimal — only attributes required for the test |
| Spec breaks when implementation changes but behavior is unchanged | Tests that break on refactoring are testing internals, not contracts |

## Integration

| Skill | When to chain |
|-------|---------------|
| **rspec-best-practices** | For general RSpec style and TDD discipline |
| **ruby-service-objects** | For the service conventions being tested |
| **ruby-api-client-integration** | For API client layer testing patterns |
| **rails-engine-testing** | When testing engine-specific services |

## Assets

- [assets/spec_examples.md](assets/spec_examples.md)
- [assets/testing_checklist.md](assets/testing_checklist.md)

Files in this skill

  • PATTERNS.md814 B
  • SKILL.md4.9 KB
  • assets/spec_examples.md1.2 KB
  • assets/testing_checklist.md508 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.