【问题标题】:How to test kernel sleep method in module in Rspec 3.5?如何在 Rspec 3.5 的模块中测试内核睡眠方法?
【发布时间】:2017-10-30 05:45:09
【问题描述】:

我在模块的私有部分有以下方法...

  def add_api_delay
    sleep(retry_delay * (retry_multiplier_adjustment - retries)) if retries.positive?
  end

到目前为止,我正在使用的规范看起来像这样......

  let!(:klass) do
    Class.new do
      include AmazonMws::Shared::Utilities
      attr_accessor :retries, :retry_multiplier_adjustment, :retry_delay
      def initialize
        @retries = 1
        @retry_delay = 1
        @retry_multiplier_adjustment = 2
      end

      def test_add_api_delay
        add_api_delay
      end
    end
  end

  describe '.add_api_delay', focus: true do
    # let(:kernel_spy) { class_spy(Kernel, sleep: true) }

    before do

    end

    it 'sleeps retry api calls' do
      # allow(klass).to receive(:sleep).with(1).and_return(kernel_spy)
      # expect(kernel_spy).to have_received(:sleep)
      # expect(Kernel).to receive(:sleep).with(1)
      expect(Kernel).to receive(:sleep).and_return(true)
      klass.new.test_add_api_delay
    end
  end

我有几个目标和原因想测试这个私有方法,但是如何验证 sleep 是否被调用。我不想减慢套件的速度,因此理想情况下我正在尝试对内核使用类间谍。我正在测试的任何东西似乎都不起作用。

更新

  describe '.add_api_delay' do
    before do
      allow_any_instance_of(klass).to receive(:sleep).and_return(1)
    end

    it 'sleeps retry api calls' do
      expect(klass.new.test_add_api_delay).to eq(1)
    end
  end

这可行,但是它并不理想,因为它会标记以下警察......

C: RSpec/AnyInstance: Avoid stubbing using allow_any_instance_of
      allow_any_instance_of(klass).to receive(:sleep).and_return(1)

你有什么想法?

【问题讨论】:

    标签: ruby-on-rails ruby rspec kernel


    【解决方案1】:

    我认为它不起作用,因为你没有把期望放在正确的事情上。看起来您已尝试将其放在 Kernel 和被测类上,但您需要将其放在实例上:

    it 'sleeps retry api calls' do
      thing = klass.new
      allow(thing).to receive(:sleep)
    
      thing.test_add_api_delay
    
      expect(thing).to have_received(:sleep).with(1)
    end
    

    上面有一种测试气味,因为它正在对被测类进行存根。但我认为这可能比在这里强制执行一些设计约束并在调用sleep 时失去一些 Ruby 的优雅要好。

    【讨论】:

      【解决方案2】:

      你应该使用allow_any_instace_of

      RSpec.describe "allow_any_instance_of" do
        it "returns the specified value on any instance of the class" do
          allow_any_instance_of(Object).to receive(:foo).and_return(:return_value)
      
          o = Object.new
          expect(o.foo).to eq(:return_value)
        end
      end
      

      【讨论】:

      【解决方案3】:

      这是调用睡眠的私有方法的 RSpec 测试的最小示例。

      class Sleeper
        def initialize(delay:)
          @delay = delay
        end
        attr_reader :delay
      
        private
      
        def rest
          sleep delay
        end
      end
      
      require 'rspec'
      
      RSpec.describe Sleeper do
        let(:sleeper) { Sleeper.new(delay: delay) }
        let(:delay) { 10 }
      
        # Comment here explaining why this test is necessary
        describe '.send :rest' do
          before { allow_any_instance_of(Sleeper).to receive(:sleep) }
      
           it 'sleeps' do
             expect(sleeper).to receive(:sleep)
             sleeper.send :rest
           end
        end
      end
      

      更新:

      你必须在这里做出决定。

      Rubocop 关于“避免存根”的建议是个好建议,但您说您有理由测试私有方法是否使用内核方法,而存根是最好的方法。如果这些测试原因比 Rubocop 纯度更重要,您应该忽略 Rubocop 警告。

      如果 Rubocop 的建议更重要,那么我建议编写一个计算延迟的公共方法,并为此编写测试。可能是这样的:

      def retry_delay_duration
        return 0 if retries < 1
        retry_delay * (retry_multiplier_adjustment - retries)
      end
      
      private
      
      def add_api_delay
        sleep retry_delay_duration
      end
      

      在第二种情况下,您应该删除add_api_delay 的测试,只测试公共方法retry_delay_duration(或任何您称之为的)返回正确的延迟。

      【讨论】:

      • 添加了一些想法,因为这个建议不起作用。还有其他想法吗?
      • 但是如果开发人员只是退出或替换睡眠,它会被覆盖吗?另外,据我了解,使用“allow_any_instance_of”是不好的做法。
      • 你必须认识并拥有你的意图。以这种方式进行测试是“实施”测试,如果有商业案例可以这样做,这并不是一个糟糕的做法。诚然,Ruby 并不是一种很好的语言来产生这种约束,但我们都必须用我们所拥有的东西来凑合。在此示例中,该对象有权将运行时暂停任意时间长度(我敢肯定,您的真实代码设置了某种最大持续时间)。如果您必须 这样做,那么围绕它进行某种实现测试似乎是明智的。它很脆弱,但是当您开始使用作业队列时,您会对其进行重构。
      • 澄清和更正:我所说的“明智”是指“聊胜于无”。我所说的“脆弱”是指“脆弱,没有人们希望的那么有用,但总比没有好。” “运行时”是指“当前线程”。
      • 此规范用于作业队列场景。目标是确保使用睡眠并接收设置。如果我在它之外测试该方法,则可以删除 sleep 命令。
      猜你喜欢
      • 2010-11-13
      • 2012-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-19
      • 1970-01-01
      • 2013-07-19
      相关资源
      最近更新 更多