rails-tdd-slices

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Rails TDD Slices

Rails TDD 切片

Use this skill when the hardest part of the task is deciding where TDD should start.
Core principle: Start at the highest-value boundary that proves the behavior with the least unnecessary setup.
当任务中最困难的部分是决定TDD应从何处开始时,使用本技能。
核心原则: 从能以最少不必要配置验证行为的最高价值边界开始。

Quick Reference

快速参考

Change typeFirst specPathWhy
API contract, params, status code, JSON shapeRequest spec
spec/requests/
Proves the real HTTP contract
Domain rule on a cohesive record or value objectModel spec
spec/models/
Fast feedback on domain behavior
Multi-step orchestration across collaboratorsService spec
spec/services/
Focuses on the workflow boundary
Enqueue/run/retry/discard behaviorJob spec
spec/jobs/
Captures async semantics directly
Critical Turbo/Stimulus or browser-visible flowSystem spec
spec/system/
Use only when browser interaction is the real risk
Engine routing, generators, host integrationEngine spec
spec/requests/
or engine path
Normal app specs miss engine wiring — see
rails-engine-testing
Bug fixReproduction specWhere the bug is observedProves the fix and prevents regression
Unsure between layersHigher boundary firstEasier to prove real behavior before drilling down
变更类型首选测试用例路径原因
API契约、参数、状态码、JSON结构Request spec
spec/requests/
验证真实的HTTP契约
内聚记录或值对象的领域规则Model spec
spec/models/
快速反馈领域行为
跨协作对象的多步骤编排Service spec
spec/services/
聚焦工作流边界
入队/运行/重试/丢弃行为Job spec
spec/jobs/
直接捕获异步语义
关键Turbo/Stimulus或浏览器可见流程System spec
spec/system/
仅当浏览器交互是真正风险时使用
引擎路由、生成器、宿主集成Engine spec
spec/requests/
或引擎路径
常规应用测试会遗漏引擎连接配置——参见
rails-engine-testing
Bug修复复现测试用例Bug出现的位置验证修复效果并防止回归
不确定选择哪一层优先选择更高层级的边界在深入细节前,更容易验证真实行为

HARD-GATE

严格规则

text
DO NOT choose the first spec based on convenience alone.
DO NOT start with a lower-level unit if the real risk is request, job, engine, or persistence wiring.
ALWAYS run the chosen spec and verify it fails for the right reason before implementation.
text
DO NOT choose the first spec based on convenience alone.
DO NOT start with a lower-level unit if the real risk is request, job, engine, or persistence wiring.
ALWAYS run the chosen spec and verify it fails for the right reason before implementation.

Process

流程

  1. Name the behavior: State the user-visible outcome or invariant to prove.
  2. Locate the boundary: Decide where the behavior is observed first: HTTP request, service entry point, model rule, job execution, engine integration, or external adapter.
  3. Pick the smallest strong slice: Choose the spec type that proves the behavior without dragging in unrelated layers.
  4. Suggest the path: Name the likely spec path using normal Rails conventions (for example
    spec/requests/...
    ,
    spec/services/...
    ,
    spec/jobs/...
    ,
    spec/models/...
    ).
  5. Write one failing example: Keep it minimal; one example is enough to open the gate.
  6. Run and validate: Confirm the failure is because the behavior is missing, not because the setup is broken.
  7. Hand off: Continue with
    rspec-best-practices
    ,
    rspec-service-testing
    ,
    rails-engine-testing
    , or the implementation skill that fits the slice.
  1. 定义行为: 明确要验证的用户可见结果或不变量。
  2. 定位边界: 确定行为首次被观察到的位置:HTTP请求、服务入口点、模型规则、任务执行、引擎集成或外部适配器。
  3. 选择最小有效切片: 选择无需引入无关层即可验证行为的测试用例类型。
  4. 建议路径: 使用标准Rails约定指定可能的测试路径(例如
    spec/requests/...
    spec/services/...
    spec/jobs/...
    spec/models/...
    )。
  5. 编写一个失败的测试示例: 保持最简;一个示例足以开启TDD流程。
  6. 运行并验证: 确认失败原因是行为缺失,而非配置错误。
  7. 交接: 继续使用
    rspec-best-practices
    rspec-service-testing
    rails-engine-testing
    或适合该切片的实现技能。

Examples

示例

Good: New JSON Endpoint

良好实践:新增JSON接口

ruby
undefined
ruby
undefined

Behavior: POST /orders validates params and returns 201 with JSON payload

Behavior: POST /orders validates params and returns 201 with JSON payload

First slice: request spec

First slice: request spec

Suggested path: spec/requests/orders/create_spec.rb

Suggested path: spec/requests/orders/create_spec.rb

RSpec.describe "POST /orders", type: :request do let(:user) { create(:user) } let(:valid_params) { { order: { product_id: create(:product).id, quantity: 1 } } }
before { sign_in user }
it "creates an order and returns 201" do post orders_path, params: valid_params, as: :json expect(response).to have_http_status(:created) expect(response.parsed_body["id"]).to be_present end end
undefined
RSpec.describe "POST /orders", type: :request do let(:user) { create(:user) } let(:valid_params) { { order: { product_id: create(:product).id, quantity: 1 } } }
before { sign_in user }
it "creates an order and returns 201" do post orders_path, params: valid_params, as: :json expect(response).to have_http_status(:created) expect(response.parsed_body["id"]).to be_present end end
undefined

Good: New Orchestration Service

良好实践:新增编排服务

ruby
undefined
ruby
undefined

Behavior: Orders::CreateOrder validates inventory, persists, and enqueues follow-up work

Behavior: Orders::CreateOrder validates inventory, persists, and enqueues follow-up work

First slice: service spec

First slice: service spec

Suggested path: spec/services/orders/create_order_spec.rb

Suggested path: spec/services/orders/create_order_spec.rb

RSpec.describe Orders::CreateOrder do subject(:result) { described_class.call(user: user, product: product, quantity: 1) }
let(:user) { create(:user) } let(:product) { create(:product, stock: 5) }
it "returns a successful result with the new order" do expect(result).to be_success expect(result.order).to be_persisted end end
undefined
RSpec.describe Orders::CreateOrder do subject(:result) { described_class.call(user: user, product: product, quantity: 1) }
let(:user) { create(:user) } let(:product) { create(:product, stock: 5) }
it "returns a successful result with the new order" do expect(result).to be_success expect(result.order).to be_persisted end end
undefined

Test Feedback Checkpoint

测试反馈检查点

After writing and running the first failing spec, pause before implementation and present the test for review:
CHECKPOINT: Test Design Review

1. Present: Show the failing spec(s) written
2. Ask:
   - Does this test cover the right behavior?
   - Is the boundary correct (request vs service vs model)?
   - Are the most important edge cases represented?
   - Is the failure reason correct (feature missing, not setup error)?
3. Confirm: Only proceed to implementation once test design is approved.
Why this matters: Implementing against a poorly designed test wastes the TDD cycle. A 2-minute review of the test now prevents a full rewrite later.
Hand off: After test design is confirmed →
rspec-best-practices
for the full TDD gate cycle.
在编写并运行首个失败测试用例后,在开始实现前暂停,提交测试供评审:
CHECKPOINT: Test Design Review

1. Present: Show the failing spec(s) written
2. Ask:
   - Does this test cover the right behavior?
   - Is the boundary correct (request vs service vs model)?
   - Are the most important edge cases represented?
   - Is the failure reason correct (feature missing, not setup error)?
3. Confirm: Only proceed to implementation once test design is approved.
为什么这很重要: 针对设计不佳的测试进行实现会浪费TDD周期。现在花2分钟评审测试,可避免后续全面重写。
交接: 测试设计确认后 → 使用
rspec-best-practices
完成完整的TDD流程。

Pitfalls

常见陷阱

PitfallWhat to do
Starting with a PORO spec because it is easyEasy ≠ high-signal — choose the boundary that proves the real behavior
Writing three spec types before running anyPick one slice, run it, prove the failure, then proceed
Defaulting to request specs for everythingSome domain rules are better proven at the model or service layer
Defaulting to model specs for controller behaviorControllers and APIs need request-level proof
Using controller specs as the default HTTP entry pointPrefer request specs unless the repo has an existing reason
Jumping to system specs too earlyReserve for critical browser flows that lower layers cannot prove
"We'll add the request spec later"The spec is the gate — implement only after the first slice is failing for the right reason
First spec requires excessive factory setupExcessive setup = wrong boundary. Simplify or move the slice.
陷阱应对方法
因为简单就从PORO测试开始简单 ≠ 高价值信号 —— 选择能验证真实行为的边界
在运行任何测试前编写三种类型的测试用例选择一个切片,运行它,验证失败原因,再继续
所有场景都默认使用Request spec某些领域规则在模型或服务层验证效果更好
针对控制器行为默认使用Model spec控制器和API需要请求级别的验证
将Controller spec作为默认的HTTP入口测试除非仓库有既定原因,否则优先使用Request spec
过早使用System spec仅保留用于低层无法验证的关键浏览器流程
"我们稍后再添加Request spec"测试是TDD的入口 —— 仅当首个切片因正确原因失败后再开始实现
首个测试用例需要过多的工厂配置过多配置 = 边界选择错误。简化或更换切片。

Integration

集成

SkillWhen to chain
rspec-best-practicesAfter choosing the first slice, to enforce the TDD loop correctly
rspec-service-testingWhen the first slice is a service object spec
rails-engine-testingWhen the first slice belongs to an engine
rails-bug-triageWhen the starting point is an existing bug report
refactor-safelyWhen the task is mostly structural and needs characterization tests first
技能何时衔接
rspec-best-practices选择首个切片后,用于正确执行TDD循环
rspec-service-testing当首个切片是服务对象测试用例时
rails-engine-testing当首个切片属于引擎时
rails-bug-triage当起点是现有Bug报告时
refactor-safely当任务主要是结构调整且需要先进行特征测试时