Class 已泄漏到另一个示例中,不能再在规范中使用

Class has leaked into another example and can no longer be used in spec

我无法在本地复制它,但出于某种原因,在 CircleCi 中进行 运行 测试时出现以下错误:

<Double Mylogger> was originally created in one example but has leaked into another example and can no longer be used. rspec-mocks' doubles are designed to only last for one example, and you need to create a new one in each example you wish to use it for.

这是我的代码的简化版本:

# frozen_string_literal: true
describe 'my_rake_task' do
  let(:my_log) { Mylogger.new }

  subject { Rake::Task['my_rake_task'].execute }

  describe 'one' do
    context 'logs' do
      let(:logs) do
        [
          ['My message one'],
          ['My message two'],
        ]
      end

      after { subject }

      it 'correctly' do
        logs.each { |log| expect(my_log).to receive(:info).with(*log) }
      end
    end
  end

  describe 'two' do
    context 'logs' do
      let(:logs) do
        [
          ['My message three'],
          ['My message four'],
        ]
      end

      after { subject }

      it 'correctly' do
        logs.each { |log| expect(my_log).to receive(:info).with(*log) }
      end
    end
  end
end

为什么说MyLogger是一个double?为什么会漏水?

错误说 MyLogger 是双精度的原因是因为它是一个。当您调用 expect(my_log).to receiveallow(my_log).to receive 时,您将实例转换为 partial-double.

至于 my_log 泄漏的原因:从您发布的代码无法判断。为了造成泄漏,您的 rake 任务或规范本身中的某些代码需要将 my_log 注入某些全局状态,like a class variable.

大多数情况下,这种事情是由在 class 变量中存储某些内容引起的。您将必须弄清楚它在哪里,以及如何清除它或避免使用 class 变量 - 它可能在您的 class 或 gem.

如果可能,使用 class 变量或外部系统导致 inter-test 问题的最佳做法是在测试之间清除此类问题。例如 ActionMailer::Base.deliveriesRails.cache 是应该清除的常见事物。如果您正在使用那些 gem,您还应该清除 Faker::UniqueGeneratorRequestStore,我相信还有更多。

找到 class 变量后,如果它在您的代码中,并且您确定 class 变量是正确的方法,您可以添加 resetclear class 方法到 class 并在 before(:each) RSpec 块中调用它 spec_helper.rbrails_helper.rb.

请注意,虽然很多东西会在测试之间自动清除(例如 RSpec 模拟),并且让您认为这是完全自动的,但实际上它通常不是。

测试将仅通过以下方式保持独立:(a) 主要使用在测试中创建的对象,并且主要只在其中存储数据,以及 (b) 确保在测试之间通过您的显式代码或在负责的 gem.

这在处理外部 third-party 系统时尤其烦人,这些系统很少提供 API 来清理暂存环境,因此即使在使用 vcr gem.