模拟下载 link / 检查 link 使用 Capybara 针对控制器方法生成

Mocking download link / checking link generation against controller methods with Capybara

在某个页面的 Capybara 功能规范中,我有一个下载 link:

download_link = find_link(expected_link_text)

我想检查生成的 link 是否是下载文件的正确文件,即它会使用正确的模型对象在我的 FileController 上调用 download()

RSpec-Rails 似乎有很多方法可以得到我想要的东西。例如,在 controller spec 中,我可以在控制器上使用普通的 RSpec 断言:

expect(controller).to receive(:download).with(expected_id)
# download_link = find_link(expected_link_text) # can't do this in a controller spec
# visit(download_link)                          # can't do this in a controller spec

在路由规范中,我可以使用 route_to():

# download_link = find_link(expected_link_text)       # can't do this in a routing spec
expect(get: download_link[href]).to route_to(controller: 'file', action: 'download', id: expected_id)

但是在功能规范中,controllerroute_to() 都不可用。

通过以下恶作剧和调试器中的大量探索,我能够将 route_to() 包含在我的测试中:

describe 'the page' do
  it 'should let the user download a file' do
    self.class.send(:include, RSpec::Rails::Matchers::RoutingMatchers)       # hack to get routing matchers into feature test
    self.class.send(:include, ActionDispatch::Assertions::RoutingAssertions) # RoutingMatchers uses this internally
    self.class.send(:define_method, :message) { |msg, _| msg }               # RoutingAssertions expects message() to be included from somewhere
    @routes = Rails.application.routes                                       # RoutingAssertions needs @routes

    download_link = find_link(expected_link_text)
    expect(get: download_link[href]).to route_to(controller: 'file', action: 'download', id: expected_id) # works!
  end
end

这确实有效,但它是香蕉。有没有现成的方法可以将 Capybara 混合到其他类型的规范中,或者将其他类型的规范混合到功能规范中?或者只是一种更简洁的 Rails-y(可能不是 RSpec)获取路线的方式?


注意: 路由没有命名,所以我不能使用 URL 助手(我不认为);由于历史原因,URL 路径本身是不连贯的噪音,所以我不只是想以字符串形式断言 href。

正如您所说,如果您想检查正在调用的特定控制器方法,那将是控制器规范,如果您想验证路由,那将是路由规范。对于 Capybara,您应该编写功能 specs/system 测试——这意味着没有 mocking/stubbing 而是 运行 端到端测试。配置您用于下载文件的任何驱动程序,然后单击 link,下载文件,并验证是否下载了正确的文件。另一种选择是只使用 url_for 而不是试图包含所有额外的东西而只做

expect(download_link[href]).to eq url_for(controller: 'file', action: 'download', id: expected_id)

或更好

expect(page).to have_link(expected_link_text, href: url_for(controller: 'file', action: 'download', id: expected_id))

但是如果您要测试文件下载,您真的应该只下载文件。

如果您必须处理编码问题,您可以使用过滤器块重写预期并解析 paths/urls 以规范化

expect(page).to have_link(expected_link_text) do |link|
  Addressable::URI.parse(link[:href]) == Addressable::URI.parse(url_for(controller: 'file', action: 'download', id: expected_id))
end