Rails 8.0.2切换缓存序列化器为MessagePack时测试报错求助
将Rails缓存序列化器从Marshal切换为MessagePack的测试问题
背景
尝试将Rails 8.0.2应用的缓存序列化器从Marshal切换为MessagePack,执行测试套件时遇到系列问题。
原测试配置
config.cache_classes = false config.cache_store = :null_store, { compress: false, serializer: ActiveSupport::Cache::SerializerWithFallback::MessagePackWithFallback } config.active_support.deprecation = :stderr config.consider_all_requests_local = true config.action_controller.perform_caching = false # only log errors during test runs to reduce IO config.log_level = :error
首次测试错误
运行测试时抛出:
ActiveSupport::MessagePack::UnserializableObjectError: Unsupported type RSpec::Mocks::Double
尝试的修改
在配置中添加MessagePack类型注册代码:
config.cache_store = :null_store, { compress: false, serializer: ActiveSupport::Cache::SerializerWithFallback::MessagePackWithFallback } factory = MessagePack::DefaultFactory pk = factory.packer uk = factory.unpacker pk.register_type(0x7f, RSpec::Mocks::Double) do |mock| mock.pack('N') end uk.register_type(0x7f) do |data| data.unpack('N') end MessagePack::DefaultFactory.register_type(0x7f, RSpec::Mocks::Double)
新错误
修改后出现:
rake aborted! NameError: undefined method `from_msgpack_ext' for class `#<Class:RSpec::Mocks::Double>' (NameError) options[:unpacker] = klass.method(unpacker).to_proc ^^^^^^^
目标使用场景
希望在以下业务场景中使用MessagePack序列化:
def results_from_user user_ids = @user.most_recent_attendances.map { |attendance| attendance["eventId"] } Rails.cache.fetch("user_attendances:#{Digest::MD5.hexdigest(user_ids.to_s)}", cache_opts) do Info.from_ids(user_ids) end end
问题分析与解决
错误根源拆解
- Mock对象进入缓存:测试环境中出现
RSpec::Mocks::Double序列化失败,是因为测试用例里有代码把Mock对象存入了缓存,而MessagePack默认不支持序列化这类测试专用对象。 - 类型注册方式错误:手动操作
MessagePack::DefaultFactory的packer/unpacker不符合ActiveSupport的集成规则——ActiveSupport封装的MessagePack要求自定义类型必须实现to_msgpack_ext和from_msgpack_ext方法,直接注册工厂会和框架逻辑冲突。
正确解决步骤
1. 优先修正测试逻辑
检查所有测试用例,确保没有将RSpec::Mocks::Double这类测试对象存入缓存。如果是测试代码的问题,直接修正:比如在测试中禁用缓存,或者用真实对象替代Mock。
2. 正确注册自定义类型(若必须处理Mock序列化)
如果确实需要在测试中序列化Mock,给RSpec::Mocks::Double添加MessagePack扩展方法并注册:
# 在测试环境配置文件中添加 RSpec::Mocks::Double.class_eval do def to_msgpack_ext # 序列化Mock的核心信息:名称和方法存根 [__name, __mock_proxy.instance_variable_get(:@method_stubs)].to_msgpack end def self.from_msgpack_ext(data) name, stubs = MessagePack.unpack(data) double = RSpec::Mocks::Double.new(name) stubs.each do |method, value| double.stub(method).and_return(value) end double end end # 注册到ActiveSupport的MessagePack配置 ActiveSupport::MessagePack.register_type(0x7f, RSpec::Mocks::Double)
3. 简化测试环境缓存配置
测试环境使用:null_store本来就不会实际存储缓存内容,可针对性关闭MessagePack序列化:
# 测试环境配置 if Rails.env.test? config.cache_store = :null_store else config.cache_store = :redis_cache_store, { compress: false, serializer: ActiveSupport::Cache::SerializerWithFallback::MessagePackWithFallback } end
4. 验证目标场景序列化有效性
针对results_from_user方法,确认Info.from_ids(user_ids)返回的Info对象可被MessagePack序列化:
- 若
Info是ActiveRecord模型,ActiveSupport已默认支持; - 若为自定义类,需确保其属性均为基本类型或已注册的可序列化类型。
内容的提问来源于stack exchange,提问作者user1022788
相关产品推荐
相关产品推荐

