RSpec stub控制器调用的MyModel类方法仅测试内生效如何解决
问题根本原因
你将MyModel.fetch_boolean('123')的调用写在了OtherModelController的类作用域下,而非控制器动作/实例方法的作用域下。
Rails在加载控制器类的阶段就会执行类作用域下的所有代码,此时你的RSpec测试的stub配置还没有运行,if MyModel.fetch_boolean('123')的判断逻辑已经提前执行完成,后续测试代码中添加的stub自然无法作用到已经执行过的逻辑上,才会出现测试上下文内stub生效、控制器实际逻辑没有命中stub的问题。
解决方案
最推荐方案:移动逻辑到实例作用域
把对应判断逻辑移动到你要调用的控制器动作(示例中是show)或者全局前置过滤器before_action中即可,修改后的控制器代码如下:
class OtherModelController < ApplicationController # 也可以写在 before_action 里,对指定动作生效 before_action :check_my_model_flag, only: :show def show # 也可以直接把判断写在动作内部 # if MyModel.fetch_boolean('123') # # do something # end render plain: 'hello' end private def check_my_model_flag if MyModel.fetch_boolean('123') # do something end end end
修改完成后,你原本的stub写法就是正确可用的,不需要调整:
allow(MyModel).to receive(:fetch_boolean).with('123').and_return(true)
其他错误写法说明
你尝试的其他写法本身就不符合语法规则,不可能生效:
allow_any_instance_of用于stub实例方法,而fetch_boolean是类方法,所以会抛出方法不存在的错误controller.stub是RSpec 2的旧语法,RSpec 3已经废弃,且语法规则要求传入方法名符号,不能直接传入方法调用的返回值,所以会报错。
特殊场景处理(确实需要类作用域判断)
如果你的业务逻辑必须将判断放在类作用域下,可以在测试配置中关闭类缓存、stub完成后强制重载控制器类,但该方案会拖慢测试速度,不推荐使用:
- 确保
config/environments/test.rb中配置为config.cache_classes = false - 在stub完成后添加重载代码:
before(:each) do allow(MyModel).to receive(:fetch_boolean).with('123').and_return(true) OtherModelController.reload end
内容的提问来源于stack exchange,提问作者Panpaper
相关产品推荐
相关产品推荐

