rspec-service-testing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRSpec Service Testing
RSpec服务测试
Use this skill when writing tests for service classes under .
spec/services/Core principle: Test the public contract (, , ), not internal implementation. Use for isolation, for integration.
.call.find.searchinstance_doublecreate当为下的服务类编写测试时使用本技能。
spec/services/核心原则: 测试公共契约(、、),而非内部实现。使用实现隔离,使用进行集成测试。
.call.find.searchinstance_doublecreateWorkflow: 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 regressionsDO NOT implement the service before step 1 is written and failing for the right reason.
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切勿在步骤1完成且因正确原因导致失败前实现服务。
Quick Reference
快速参考
| Aspect | Rule |
|---|---|
| File location | |
| Subject | |
| Unit isolation | |
| Integration | |
| Multi-assertion | |
| State verification | |
| Time-dependent | |
| API responses | FactoryBot hash factories ( |
| 方面 | 规则 |
|---|---|
| 文件位置 | |
| 测试主体 | |
| 单元隔离 | 为协作者使用 |
| 集成测试 | 针对数据库相关测试使用 |
| 多断言 | 使用 |
| 状态验证 | 使用 |
| 时间相关逻辑 | 使用 |
| API响应 | 使用FactoryBot哈希工厂( |
Spec Template
测试用例模板
ruby
undefinedruby
undefinedfrozen_string_literal: true
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
endend
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 for integration tests:
createruby
let(:source_shelter) { create(:shelter, :with_animals) }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
endend
end
使用`instance_double`实现单元隔离:
```ruby
let(:client) { instance_double(Api::Client) }
before { allow(client).to receive(:execute_query).and_return(api_response) }使用进行集成测试:
createruby
let(:source_shelter) { create(:shelter, :with_animals) }FactoryBot Hash Factories for API Responses
用于API响应的FactoryBot哈希工厂
When testing API clients, use with to build hash-shaped response fixtures — see PATTERNS.md for the full pattern and factory placement.
class: Hashinitialize_with测试API客户端时,结合和构建哈希格式的响应测试数据——完整模式和工厂放置位置请参阅PATTERNS.md。
class: Hashinitialize_withNew Test File Checklist
新测试文件检查清单
- defined for the main action
subject - for unit /
instance_doublefor integrationcreate - Happy path for each public method
- Error and edge cases (blank input, invalid refs, failures)
- Partial success scenarios where relevant
- for repeated patterns
shared_examples - for multi-assertion tests
aggregate_failures - matchers for state verification
change - Logger expectations for error logging
- 为主要动作定义
subject - 单元测试使用/ 集成测试使用
instance_doublecreate - 为每个公共方法编写正常路径测试
- 错误和边缘场景测试(空输入、无效引用、失败情况)
- 相关的部分成功场景测试
- 为重复模式使用
shared_examples - 为多断言测试使用
aggregate_failures - 为状态验证使用匹配器
change - 针对错误日志添加日志器断言
Common Mistakes
常见错误
| Mistake | Correct approach |
|---|---|
| No error scenario tests | Happy path only = false confidence — always test failures |
| Use |
| 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 |
| 技能 | 何时关联使用 |
|---|---|
| rspec-best-practices | 用于通用RSpec风格和TDD规范 |
| ruby-service-objects | 用于被测试的服务约定 |
| ruby-api-client-integration | 用于API客户端层测试模式 |
| rails-engine-testing | 测试引擎专属服务时使用 |