如何测试推送Gems的Ruby脚本?
解决方案
1. 替换system!方法,模拟子命令执行
既然脚本核心依赖system!调用外部命令,直接替换这个方法的实现,就能在测试中拦截并验证命令是否正确执行,不需要真的调用gem命令或发起HTTP请求。
以RSpec测试为例:
# spec/release_spec.rb require_relative '../release' RSpec.describe 'release script' do let(:recorded_commands) { [] } before do # 替换原system!方法,记录调用的命令 stub_const('Kernel', Module.new do def system!(command) recorded_commands << command puts command # 保留原脚本的输出行为 end end) # 拦截File.delete,验证删除操作 allow(File).to receive(:delete).and_call_original end it '按正确顺序执行操作' do load '../release' # 验证命令执行顺序 expect(recorded_commands).to eq(['gem build', 'gem push *.gem']) # 验证删除gem文件的操作被触发两次 expect(File).to have_received(:delete).twice end end
这种方式完全在Ruby层面拦截,能精准验证脚本的逻辑流程:先删除本地gem文件、再构建、再推送、最后再次删除gem文件。
2. 使用gem命令的本地模式+临时源
如果需要更贴近真实环境的测试(但不推送到真实Rubygems仓库),可以利用gem命令的参数指定临时本地源:
- 先创建临时目录并生成gem索引:
mkdir -p /tmp/test_gem_repo gem generate_index -d /tmp/test_gem_repo - 修改脚本中的
gem push命令,指向临时源:system!("gem push *.gem --source /tmp/test_gem_repo")
这种方式能测试gem build和gem push的真实执行流程,且不会影响真实的Rubygems仓库。
3. 拦截子命令的网络请求
如果需要验证HTTP请求内容,可以通过环境变量让子进程加载网络拦截代码:
- 创建拦截脚本
mock_rubygems.rb:require 'webmock' WebMock.enable! # 拦截Rubygems的推送请求 stub_request(:post, "https://rubygems.org/api/v1/gems.json") .to_return(status: 201, body: "", headers: {}) - 运行脚本时通过
RUBYOPT注入拦截逻辑:RUBYOPT="-r ./mock_rubygems.rb" ruby release
这样gem push发起的HTTP请求会被拦截,不会发送到真实的Rubygems服务器,适合需要验证请求细节的场景。
内容的提问来源于stack exchange,提问作者Dorian
相关产品推荐
相关产品推荐

