如何在Feature Test中测试文件下载功能?
Capybara+Selenium 测试 send_data 文件下载方案
我在 Controller 里用 send_data 实现了 CSV 数据下载功能,现在要写 Feature Test 验证这个功能,用的是 Capybara 搭配 Selenium(Chrome 浏览器),有两个核心问题要解决:
- 怎么确认文件已经完成下载?
- 如何避免文件跑到本地默认下载文件夹?比如把下载拦截存到测试专属文件夹方便检查,或者直接捕获响应数据不生成实际文件?
我的文件生成代码:
send_data csv_data, type: 'text/csv; charset=utf-8; header=present', disposition: "attachment; filename=<filename>"
问题1:检查文件是否完成下载
先给 Chrome 配置好指定的测试下载目录,之后通过等待目标文件出现在这个目录里,就能判断下载完成:
- 先在测试的初始化阶段配置 Chrome 下载偏好:
DOWNLOAD_PATH = Rails.root.join('tmp', 'test_downloads').to_s Capybara.register_driver :selenium_chrome_download do |app| prefs = { download: { prompt_for_download: false, default_directory: DOWNLOAD_PATH, directory_upgrade: true } } options = Selenium::WebDriver::Chrome::Options.new options.add_preference(:download, prefs) Capybara::Selenium::Driver.new(app, browser: :chrome, options: options) end # 测试中使用这个自定义 Driver Capybara.default_driver = :selenium_chrome_download - 测试流程里的验证逻辑:
# 先清空测试下载目录,避免旧文件干扰 FileUtils.rm_rf(Dir.glob("#{DOWNLOAD_PATH}/*")) # 触发下载操作,比如点击页面上的导出按钮 click_button '导出CSV' # 等待文件出现,设置超时时间防止无限等待 timeout = 10 end_time = Time.now + timeout downloaded_file = nil until downloaded_file || Time.now > end_time downloaded_file = Dir.glob("#{DOWNLOAD_PATH}/*.csv").first sleep 0.5 end # 断言文件存在,即下载完成 expect(downloaded_file).not_to be_nil
问题2:拦截下载或指定测试目录
上面的配置已经把下载目录指定到了项目的 tmp/test_downloads,完全不会用到本地默认下载文件夹。如果想更高效,直接捕获响应数据不生成实际文件,可以绕过浏览器,直接请求接口拿响应:
# 先模拟登录(如果接口需要权限验证) sign_in user # 直接发送请求获取响应,不需要走浏览器渲染流程 get export_csv_path # 先断言响应头是否符合预期 expect(response.headers['Content-Type']).to eq 'text/csv; charset=utf-8; header=present' expect(response.headers['Content-Disposition']).to include 'attachment; filename=' # 直接检查响应体内容,验证CSV数据是否正确 expect(response.body).to include '预期的CSV表头或内容'
这种方式跳过了浏览器下载的IO操作,速度更快,也不用处理文件清理的问题。
内容的提问来源于stack exchange,提问作者Hywel Griffiths
相关产品推荐
相关产品推荐

