You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Turbo+Stimulus即时搜索在Capybara测试中无法触发JS动作

问题描述

我基于Turbo和Stimulus控制器实现了即时输入搜索功能,相关JavaScript代码位于app/javascript/controllers/form_submission_controller.js:

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {

  search() {
    clearTimeout(this.timeout)
    this.timeout = setTimeout(() => {
      this.element.requestSubmit()
    }, 200)
  }
}

视图代码如下:

<%= form_with(url: users_path, method: :get, data: { controller: "form-submission", turbo_frame: "users", turbo_action: "advance" }) do |form| %>
  <%= form.label :query, "Search by name:" %>
  <%= form.text_field :query, data: { action: "input->form-submission#search" } %>
<% end %>

该功能在浏览器中正常运行,但在使用RSpec + Capybara(Poltergeist驱动)的测试套件中,调用fill_in输入查询时,无法触发Stimulus控制器更新页面,页面仍显示完整列表,且日志中无搜索HTTP请求。如何在测试套件中触发该控制器函数?

解决方案

1. 手动触发输入事件并等待异步操作

Poltergeist基于PhantomJS,对现代前端事件的模拟存在局限,fill_in可能不会自动触发Stimulus绑定的input事件,或者定时器延迟导致测试未等请求完成就执行断言。可以手动触发事件并配合Capybara的断言等待:

fill_in "Search by name:", with: "test"
# 手动触发input事件
page.execute_script("document.querySelector('[data-action=\"input->form-submission#search\"]').dispatchEvent(new Event('input'))")
# 用断言等待页面更新,替代固定时长sleep
expect(page).to have_selector("#users .user", count: 1) # 替换为实际的结果选择器

2. 替换Poltergeist为现代驱动

Poltergeist已停止维护,对Hotwire(Turbo/Stimulus)的支持不足,建议切换到更适配的驱动:

  • Selenium + Chrome/Firefox Headless:完全模拟真实浏览器行为,对前端框架支持更好
  • Capybara-webkit:仍在维护,轻量且支持现代JS特性

以Selenium+Chrome为例,配置步骤:

  1. 在Gemfile添加依赖:
group :test do
  gem 'capybara'
  gem 'selenium-webdriver'
  gem 'webdrivers' # 自动管理浏览器驱动版本
end
  1. 在spec/rails_helper.rb中配置驱动:
Capybara.register_driver :selenium_chrome_headless do |app|
  options = Selenium::WebDriver::Chrome::Options.new
  options.add_argument('--headless=new')
  options.add_argument('--disable-gpu')
  options.add_argument('--no-sandbox')
  Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
end

Capybara.default_driver = :selenium_chrome_headless
Capybara.javascript_driver = :selenium_chrome_headless

切换驱动后,fill_in通常能自动触发input事件和Stimulus逻辑。

3. 直接调用Stimulus控制器方法

如果前两种方法无效,可以通过JS直接调用控制器的search方法:

fill_in "Search by name:", with: "test"
# 获取控制器实例并执行搜索方法
page.execute_script("
  const formElement = document.querySelector('[data-controller=\"form-submission\"]')
  const controller = Stimulus.controllers.find(c => c.element === formElement)
  controller.search()
")
# 等待页面更新
expect(page).to have_content("匹配的搜索结果") # 替换为实际的结果标识

4. 优化测试等待逻辑

Capybara默认不会等待异步操作完成,需用显式断言替代sleep,让测试自动等待直到条件满足:

fill_in "Search by name:", with: "test"
page.execute_script("document.querySelector('[data-action=\"input->form-submission#search\"]').dispatchEvent(new Event('input'))")
# 等待搜索结果出现/总数变化
expect(page).to have_selector(".user", count: 1)

内容的提问来源于stack exchange,提问作者vinnydiehl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 18:01:21