Ruby VCR测试无法捕获Sinatra内部HTTP调用的解决方法
问题
我开发了一个Sinatra应用,包含以下路由调用:
get '/test' do @res = Net::HTTP.get_response(URI('http://www.iana.org/domains/reserved')) erb :test end
对应的视图文件内容:
<%= @res.body %>
我使用Capybara + Selenium + VCR对该功能进行测试,测试可以通过,但VCR并未生成对应的cassette,无法实现测试提速。测试代码如下:
#!/usr/bin/env ruby require 'test/unit' require 'capybara' require 'capybara/dsl' require 'webdrivers/chromedriver' require 'webmock' require 'vcr' require 'dotenv' require_relative "../sinatra_app.rb" class SinatraAppTest < Test::Unit::TestCase include Capybara::DSL def setup Dotenv.load "#{__dir__}/../.env" Capybara.register_driver :chrome do |app| Capybara::Selenium::Driver.new(app, :browser => :chrome, options: chrome_options) end Capybara.default_driver = :chrome Capybara.default_max_wait_time = 20 Capybara.app_host = "http://localhost:9393" VCR.configure do |config| config.cassette_library_dir = "fixtures/vcr_cassettes2" config.hook_into :webmock config.allow_http_connections_when_no_cassette = true config.default_cassette_options = {re_record_interval: (3600 * (24 * 30))} config.ignore_localhost = true end end # Settings and profile for the Chrome Browser # NOTE: still cannot get headless working def chrome_options opts = Selenium::WebDriver::Chrome::Options.new # opts.add_argument('--headless') unless ENV['UI'] opts.add_argument('--no-sandbox') opts.add_argument('--disable-gpu') opts.add_argument('--disable-dev-shm-usage') opts.add_argument('--window-size=1920,1080') opts end def test_iana VCR.use_cassette("iana") do visit "/test" assert page.has_content?("Example domains") end end def teardown Capybara.reset_sessions! Capybara.use_default_driver end end
核心问题:如何捕获Sinatra应用内部发起的HTTP调用并通过VCR进行录制?(注:若将Net::HTTP调用放在测试代码内,VCR可正常录制,但这并非我期望的测试方式)
解决方案
原因分析
使用Selenium驱动时,Sinatra应用会以独立进程的形式启动(Chrome浏览器通过HTTP请求访问该进程),而VCR的钩子仅在测试进程中生效,无法捕获到Sinatra进程内部发起的外部HTTP请求。
解决方法
方法1:改用Capybara RackTest驱动(推荐)
RackTest驱动无需启动独立服务器,会直接在测试进程中调用Sinatra应用代码,因此VCR可以正常捕获应用内部的HTTP请求。
修改测试代码的setup方法,替换默认驱动为:rack_test:
def setup Dotenv.load "#{__dir__}/../.env" # 移除Chrome驱动注册,改用默认的RackTest驱动 Capybara.default_driver = :rack_test Capybara.default_max_wait_time = 20 # 无需设置app_host,RackTest直接调用应用实例 VCR.configure do |config| config.cassette_library_dir = "fixtures/vcr_cassettes2" config.hook_into :webmock config.allow_http_connections_when_no_cassette = true config.default_cassette_options = {re_record_interval: (3600 * (24 * 30))} config.ignore_localhost = true end end
方法2:保留Selenium驱动(需跨进程共享VCR配置)
如果必须使用Selenium(例如需要测试JavaScript交互),需要让Sinatra应用进程也加载VCR配置,确保请求能被捕获:
- 在Sinatra应用文件(
sinatra_app.rb)中添加VCR和WebMock的配置:
require 'vcr' require 'webmock' VCR.configure do |config| config.cassette_library_dir = "fixtures/vcr_cassettes2" config.hook_into :webmock config.allow_http_connections_when_no_cassette = true config.default_cassette_options = {re_record_interval: (3600 * (24 * 30))} end
- 在测试代码中,确保VCR的cassette使用支持跨进程的模式,例如添加
:record => :new_episodes(按需调整):
def test_iana VCR.use_cassette("iana", :record => :new_episodes) do visit "/test" assert page.has_content?("Example domains") end end
这种方法需要注意文件权限和cassette的读写冲突,建议仅在必须使用Selenium时采用。
内容的提问来源于stack exchange,提问作者map7
相关产品推荐
相关产品推荐

