inertia-rails-testing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseInertia Rails Testing
Inertia Rails 测试
Testing patterns for Inertia responses with RSpec and Minitest.
For each controller action, verify:
- Correct component →
render_component('users/index') - Expected props →
have_props(users: satisfy { ... }) - No leaked data →
have_no_prop(:secret) - Flash messages → then
follow_redirect!have_flash(notice: '...') - Deferred props →
have_deferred_props(:analytics)
Common mistake: Forgetting after PRG — without it, you're
asserting against the 302 redirect response, not the Inertia page that follows.
follow_redirect!使用RSpec和Minitest测试Inertia响应的模式。
针对每个控制器动作,需验证:
- 正确的组件 →
render_component('users/index') - 预期的属性 →
have_props(users: satisfy { ... }) - 无数据泄露 →
have_no_prop(:secret) - Flash消息 → 先调用,再使用
follow_redirect!have_flash(notice: '...') - 延迟属性 →
have_deferred_props(:analytics)
常见错误: PRG模式(Post/Redirect/Get)后忘记调用——如果不调用,你是在对302重定向响应而非后续的Inertia页面进行断言。
follow_redirect!Setup
设置
ruby
undefinedruby
undefinedspec/rails_helper.rb
spec/rails_helper.rb
require 'inertia_rails/rspec'
undefinedrequire 'inertia_rails/rspec'
undefinedRSpec Matchers
RSpec 匹配器
| Matcher | Purpose |
|---|---|
| Verify response is Inertia format |
| Check rendered component name |
| Partial props match |
| Exact props match |
| Assert prop absent |
| Partial flash match |
| Exact flash match |
| Assert flash absent |
| Check deferred props exist |
| Partial view_data match |
| 匹配器 | 用途 |
|---|---|
| 验证响应为Inertia格式 |
| 检查渲染的组件名称 |
| 属性部分匹配 |
| 属性完全匹配 |
| 断言属性不存在 |
| Flash消息部分匹配 |
| Flash消息完全匹配 |
| 断言Flash消息不存在 |
| 检查延迟属性是否存在 |
| view_data部分匹配 |
RSpec Examples
RSpec 示例
ALWAYS use matchers (, , ) instead of
direct property access (, ):
render_componenthave_propshave_flashinertia.componentinertia.props[:key]ruby
undefined请始终使用匹配器(、、),而非直接访问属性(、):
render_componenthave_propshave_flashinertia.componentinertia.props[:key]ruby
undefinedBAD — direct property access:
错误示例 —— 直接访问属性:
expect(inertia.component).to eq('users/index')
expect(inertia.component).to eq('users/index')
expect(inertia.props[:users].length).to eq(3)
expect(inertia.props[:users].length).to eq(3)
GOOD — use matchers:
正确示例 —— 使用匹配器:
expect(inertia).to render_component('users/index')
expect(inertia).to have_props(users: satisfy { |u| u.length == 3 })
```rubyexpect(inertia).to render_component('users/index')
expect(inertia).to have_props(users: satisfy { |u| u.length == 3 })
```rubyKey pattern: follow_redirect! after POST/PATCH/DELETE before asserting
核心模式:POST/PATCH/DELETE请求后,先调用follow_redirect!再进行断言
it 'redirects with flash on success' do
post users_path, params: { user: valid_params }
expect(response).to redirect_to(users_path)
follow_redirect!
expect(inertia).to have_flash(notice: 'User created!')
end
it 'returns validation errors on failure' do
post users_path, params: { user: { name: '' } }
follow_redirect!
expect(inertia).to have_props(errors: hash_including('name' => anything))
end
undefinedit '重定向并返回成功提示Flash' do
post users_path, params: { user: valid_params }
expect(response).to redirect_to(users_path)
follow_redirect!
expect(inertia).to have_flash(notice: '用户已创建!')
end
it '验证失败时返回错误信息' do
post users_path, params: { user: { name: '' } }
follow_redirect!
expect(inertia).to have_props(errors: hash_including('name' => anything))
end
undefinedTest Shared Props
测试共享属性
Shared props from are included in . The
helper is available in specs after requiring :
inertia_shareinertia.propsinertiatype: :requestinertia_rails/rspecruby
it 'includes shared auth data' do
sign_in(user)
get dashboard_path
expect(inertia).to have_props(
auth: hash_including(user: hash_including(id: user.id))
)
endruby
it 'excludes auth data for unauthenticated users' do
get dashboard_path
expect(inertia).to have_props(auth: hash_including(user: nil))
end来自的共享属性会包含在中。引入后,辅助方法可在测试中使用:
inertia_shareinertia.propsinertia_rails/rspecinertiatype: :requestruby
it '包含共享的认证数据' do
sign_in(user)
get dashboard_path
expect(inertia).to have_props(
auth: hash_including(user: hash_including(id: user.id))
)
endruby
it '未认证用户不返回认证数据' do
get dashboard_path
expect(inertia).to have_props(auth: hash_including(user: nil))
endTest Deferred Props
测试延迟属性
ruby
it 'defers expensive analytics data' do
get dashboard_path
expect(inertia).to have_deferred_props(:analytics, :statistics)
expect(inertia).to have_deferred_props(:slow_data, group: :slow)
endruby
it '延迟加载耗时的分析数据' do
get dashboard_path
expect(inertia).to have_deferred_props(:analytics, :statistics)
expect(inertia).to have_deferred_props(:slow_data, group: :slow)
endPartial Reload Helpers
部分重载辅助方法
ruby
it 'supports partial reload' do
get users_path
# Simulate partial reload — only fetch specific props
inertia_reload_only(:users, :pagination)
# Or exclude specific props
inertia_reload_except(:expensive_stats)
# Load deferred props
inertia_load_deferred_props(:default)
inertia_load_deferred_props # loads all groups
endruby
it '支持部分重载' do
get users_path
# 模拟部分重载 —— 仅获取指定属性
inertia_reload_only(:users, :pagination)
# 或排除指定属性
inertia_reload_except(:expensive_stats)
# 加载延迟属性
inertia_load_deferred_props(:default)
inertia_load_deferred_props # 加载所有分组
endTest External Redirects (inertia_location)
测试外部重定向(inertia_location)
ruby
it 'redirects to Stripe via inertia_location' do
post checkout_path
# inertia_location returns 409 with X-Inertia-Location header
expect(response).to have_http_status(:conflict)
expect(response.headers['X-Inertia-Location']).to match(/stripe\.com/)
endruby
it '通过inertia_location重定向至Stripe' do
post checkout_path
# inertia_location会返回409状态码及X-Inertia-Location请求头
expect(response).to have_http_status(:conflict)
expect(response.headers['X-Inertia-Location']).to match(/stripe\.com/)
endNEVER Test These
绝对不要测试这些内容
- Inertia framework behavior — don't test that sends CSRF tokens or that
<Form>works. Test YOUR controller logic.router.visit - Exact prop structure — use , not deep equality on full JSON. Brittle tests break when you add a column.
have_props(users: satisfy { ... }) - Flash without — after POST/PATCH/DELETE with redirect, you MUST
follow_redirect!before asserting flash or props. Without it you're asserting against the 302, not the page.follow_redirect! - Deferred prop values on initial load — deferred props are in the initial response because they're fetched in a separate request after page render. Use
nilto verify they're registered, orhave_deferred_props(:key)to resolve them in tests.inertia_load_deferred_props - Mocked Inertia responses — use specs that exercise the full stack.
type: :requestspecs withtype: :controllerdon't work because Inertia uses a custom render pipeline that only executes in the full request cycle.assigns
- Inertia框架本身的行为 —— 不要测试是否发送CSRF令牌,或
<Form>是否正常工作。只需测试你自己的控制器逻辑。router.visit - 属性的精确结构 —— 使用,而非对完整JSON进行深度相等断言。这种脆弱的测试会在你添加列时失效。
have_props(users: satisfy { ... }) - 未调用follow_redirect!时的Flash —— 在POST/PATCH/DELETE请求并重定向后,必须先调用,再断言Flash或属性。否则你是在对302响应而非目标页面进行断言。
follow_redirect! - 初始加载时的延迟属性值 —— 初始响应中延迟属性为,因为它们会在页面渲染后通过单独请求获取。使用
nil验证它们已注册,或使用have_deferred_props(:key)在测试中解析它们。inertia_load_deferred_props - 模拟Inertia响应 —— 使用测试来覆盖完整调用栈。
type: :request测试搭配type: :controller无法正常工作,因为Inertia使用自定义渲染管道,仅在完整请求周期中执行。assigns
Direct Access (use matchers above when possible)
直接访问(尽可能使用上述匹配器)
ruby
inertia.component # => 'users/index'
inertia.props # => { users: [...] }
inertia.props[:users] # direct prop access
inertia.flash # => { notice: 'Created!' }
inertia.deferred_props # => { default: [:analytics], slow: [:report] }ruby
inertia.component # => 'users/index'
inertia.props # => { users: [...] }
inertia.props[:users] # 直接访问属性
inertia.flash # => { notice: '已创建!' }
inertia.deferred_props # => { default: [:analytics], slow: [:report] }Related Skills
相关技能
- Controller patterns → (prop types, flash, PRG)
inertia-rails-controllers - Form flows → (submission, validation)
inertia-rails-forms - Deferred/shared props → (Deferred, usePage)
inertia-rails-pages
- 控制器模式 → (属性类型、Flash、PRG)
inertia-rails-controllers - 表单流程 → (提交、验证)
inertia-rails-forms - 延迟/共享属性 → (Deferred、usePage)
inertia-rails-pages
References
参考资料
MANDATORY — READ ENTIRE FILE when writing Minitest tests (not RSpec):
(~40 lines) — Minitest assertions
equivalent to the RSpec matchers above.
references/minitest.mdDo NOT load for RSpec projects — the matchers above are all
you need.
minitest.md必看 —— 编写Minitest测试时请完整阅读此文件(非RSpec测试):
(约40行)—— 与上述RSpec匹配器对应的Minitest断言方法。
references/minitest.md请勿在RSpec项目中引入——上述匹配器已完全满足需求。
minitest.md