模拟补丁模块

Mocking a patch module

我有一个代码库,它在两种情况下进行了测试:运行 通过入口点 A 和 B。当它是通过 A 的 运行 时,数据库连接按原样使用。当它是 运行 通过 B 时,ActiveRecord::Base.connection 是猴子补丁。

由于 B 只是一个辅助脚本,目前在 rspec 中通过 运行 将其作为外部命令测试并检查输出。不过,我想恢复理智并测试行为而不产生新进程。

有没有办法在 rspec mocks 到 "temporarily extend" a class?我想获得以下行为:

before do
  ActiveRecord::Base.connection.extend(App::SomePatch)
end
after do
  ActiveRecord::Base.connection.unextend(App::SomePatch)
end

unextend当然不存在。我只有 3 个方法需要修补,所以我可以为每个方法使用模拟,但是方法别名使这变得复杂。

补丁模块如下所示:

module SomePatch
  def SomePatch.included(mod)
    alias :old_execute :execute
  end

  def execute(*args) ... end

  def some_storage
    @some_storage ||= []
  end
end

我会选择克隆,大致如下:

before do
  @original_connection = ActiveRecord::Base.connection
  ActiveRecord::Base.connection = @original_commention.dup
  ActiveRecord::Base.connection.extend(App::SomePatch)
end

after do
  ActiveRecord::Base.connection = @original_connection
end

我没有测试过,但只要没有 "quirks" 克隆对象,这应该没问题。

编辑:好的,这行不通,因为没有 connection= 方法,所以您可以尝试模拟:

before do
  @original_connection = ActiveRecord::Base.connection
  new_connection = @original_connection.dup
  new_connection.extend(App::SomePatch)
  allow(ActiveRecord::Base).to receive(:connection).and_return(new_connection)
end

而且您可能不需要 after 因为模拟将是 "undone"