You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题分析与解决

错误根源拆解

  1. Mock对象进入缓存:测试环境中出现RSpec::Mocks::Double序列化失败,是因为测试用例里有代码把Mock对象存入了缓存,而MessagePack默认不支持序列化这类测试专用对象。
  2. 类型注册方式错误:手动操作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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 03:49:55