如何用RSPEC测试缓存在常量中的Sidekiq::Limiter方法调用
问题根因
你遇到的测试失败是因为Ruby类的常量是在类加载阶段完成初始化的,早于所有测试用例的Mock逻辑执行时机。你写的验证Sidekiq::Limiter.receive(:concurrent)的用例执行时,Processier::LIMITER已经完成赋值,自然捕获不到更早的concurrent方法调用。
可落地解决方案
方案1:将常量改为懒加载实现(业务侵入最小,推荐)
修改原有常量为类级别的懒加载方法,避免类加载阶段就初始化限流器:
class Processier include Sidekiq::Worker def self.limiter @limiter ||= Sidekiq::Limiter.concurrent('analytics', 1, wait_timeout: 5, lock_timeout: 120) end def perform uploaded_submissions.each do |submission| self.class.limiter.within_limit do ProcessDownloadedFiles.perform_async(*submission) end end end end
对应测试用例可以直接捕获方法调用:
# RSpec 示例 it '调用了参数正确的并发限流器' do expect(Sidekiq::Limiter).to receive(:concurrent).with( 'analytics', 1, wait_timeout: 5, lock_timeout: 120 ).and_call_original Processier.new.perform end
方案2:测试阶段强制重置常量(零业务侵入)
如果不想修改业务代码,可以在测试执行前清除已经初始化的常量,强制触发concurrent方法重新调用:
# RSpec 示例 it '验证Sidekiq::Limiter收到concurrent调用' do # 提前清除已初始化的常量 Processier.send(:remove_const, :LIMITER) if Processier.const_defined?(:LIMITER) # 设置mock期望 expect(Sidekiq::Limiter).to receive(:concurrent).with( 'analytics', 1, wait_timeout: 5, lock_timeout: 120 ) # 引用常量触发初始化,或者直接执行perform逻辑 Processier.new.perform end # 测试结束后恢复常量,避免影响其他用例 after do Processier.send(:remove_const, :LIMITER) if Processier.const_defined?(:LIMITER) Processier.const_set(:LIMITER, Sidekiq::Limiter.concurrent('analytics', 1, wait_timeout: 5, lock_timeout: 120)) end
方案3:直接Stub限流器实例
如果你的测试目标是验证within_limit逻辑被正确调用,不需要校验concurrent方法的入参,可以直接Stub常量:
# RSpec 示例 it '正确通过限流器执行异步任务' do mock_limiter = instance_double(Sidekiq::Limiter::Concurrent) stub_const("Processier::LIMITER", mock_limiter) # 假设本次执行有3个submission,验证限流器被调用3次 expect(mock_limiter).to receive(:within_limit).exactly(3).times.and_yield Processier.new.perform end
内容的提问来源于stack exchange,提问作者fatfrog
相关产品推荐
相关产品推荐

