Capybara Minitest中has_field?可用但assert_field报错问题排查
页面存在如下input元素:
<input value="" data-autofocus="true" class="form-control" type="text" name="user[login]" id="user_login" />
用Capybara搭配Minitest(驱动为:rack_test)时,has_field?("user_login")能正常找到这个元素,但调用assert_field("user_login")却报错:
# has_field? 执行结果 has_field?("user_login") # => true # assert_field 执行结果 assert_field("user_login") # eval error: Unused parameters passed to Capybara::Queries::SelectorQuery : [:field, "user_login"] # /home/vagrant/.rvm/gems/ruby-3.0.4/gems/capybara-3.37.1/lib/capybara/queries/selector_query.rb:52:in `initialize' # /home/vagrant/.rvm/gems/ruby-3.0.4/gems/capybara-3.37.1/lib/capybara/node/matchers.rb:842:in `new' # /home/vagrant/.rvm/gems/ruby-3.0.4/gems/capybara-3.37.1/lib/capybara/node/matchers.rb:842:in `_verify_selector_result' # /home/vagrant/.rvm/gems/ruby-3.0.4/gems/capybara-3.37.1/lib/capybara/node/matchers.rb:110:in `assert_selector' # /home/vagrant/.rvm/gems/ruby-3.0.4/gems/capybara-3.37.1/lib/capybara/session.rb:771:in `assert_selector' # /home/vagrant/.rvm/gems/ruby-3.0.4/gems/capybara-3.37.1/lib/capybara/dsl.rb:52:in `call' # /home/vagrant/.rvm/gems/ruby-3.0.4/gems/capybara-3.37.1/lib/capybara/dsl.rb:52:in `assert_selector' # /home/vagrant/.rvm/gems/ruby-3.0.4/gems/capybara-3.37.1/lib/capybara/minitest.rb:288:in `block (2 levels) in <module:Assertions>' # (rdbg)//vagrant/mo/test/integration/capybara_student_test.rb:1:in `block in test_creating_drafts' # => nil
根据官方文档,Capybara::Minitest::Assertions#assert_field是基于Capybara::Node::Matchers#has_field?实现的,我已经引入了Capybara::Minitest::Assertions并使用ActionDispatch::Testing::Integration::Capybara gem,搞不懂为啥两者表现不一样。
问题出在assert_field的参数规则和has_field?不一样。has_field?的第一个参数直接传字段的匹配值(比如ID、名称),但Minitest的assert_field第一个参数是断言失败时的提示消息,字段匹配条件要放在后面的选项里。
正确的调用方式有几种:
- 直接指定id选项:
assert_field nil, id: "user_login" - 如果字段有对应的label标签,也可以把标签文本作为第一个参数,再指定id:
# 假设页面有<label for="user_login">登录账号</label> assert_field "登录账号", id: "user_login" - 更简洁的写法(Capybara 3.x+支持):
assert_field :user_login
另外,你也可以用assert_selector替代,它的参数规则和has_field?完全一致:
assert_selector :field, "user_login"
本质原因是Minitest的断言方法普遍遵循assert_*(message, *args)的签名,而Capybara的has_*?方法是has_*?(selector, options),两者参数顺序不兼容。你之前直接把ID传给assert_field的第一个参数,它被当成了错误提示消息,真正的选择器参数没传对,才触发了参数错误。
内容的提问来源于stack exchange,提问作者nimmolo

