如何仅监视 ActiveSupport::Notifications #instrument 的一个调用,而不是全部

How to spy just one call of ActiveSupport::Notifications #instrument, not all of them

我正在做一个 Rspec 测试来检查 ActiveSupport::Notification.instrument 是否被调用了一些参数。

问题是为了使这个测试需要 FactoryBot 构建一些对象,但是当我试图监视 ActiveSupport::Notification.instrument 时,我总是得到一个错误:

ActiveSupport::Notifications received :instrument with unexpected arguments
         expected: (:asd)
              got: ("factory_bot.run_factory", {:factory=>#<FactoryBot::Factory:0x005569b6d30, @al... nil, dispatch: nil, distribution_state: 2, main_category_id: nil>}, :strategy=>:build, :traits=>[]})

FactoryBot 似乎调用了 activesupport,所以当我出于测试目的模拟它时,我最终模拟得太远了...

代码示例:

class:

class SomeObject
    def initialize(something)
        #some code
    end

    def my_method
        ActiveSupport::Notifications.instrument :asd
    end
end

规格:

describe "#my_method" do
    let(:some_object) { build :some_object }
    before do
      allow(ActiveSupport::Notifications).to receive(:instrument).with :asd
    end

    it "calls notifier" do
      described_class.new(some_object).my_method

      expect(ActiveSupport::Notifications).to have_received(:instrument).with :asd
    end
  end

我怎样才能模拟我的调用而不是 FactoryBot 的调用。

我只在嘲笑 :asd 之前再用一个 allow 来管理它:

 allow(ActiveSupport::Notifications).to receive(:instrument).and_call_original

还有其他(更好的)方法吗?

我通常会避免嘲笑。

我遇到了类似的问题,下面是我如何解决的:

  describe "#my_method" do
    let(:some_object) { build :some_object }

    before { record_events }

    it "calls notifier" do
      described_class.new(some_object).my_method

      # Make sure your event was triggered
      expect(events.map(&:name)).to include('asd')

      # Check number of events
      expect(events).to be_one

      # Check contents of event payload                  
      expect(events.first.payload).to eq({ 'extra' => 'context' })

      # Even check the duration of an event
      expect(events.first.duration).to be < 3
    end

    private

    attr_reader :events

    def record_events
      @events = []
      ActiveSupport::Notifications.subscribe(:asd) do |*args| #
        @events << ActiveSupport::Notifications::Event.new(*args)
      end
    end
  end

相对于模拟的优势

  • 不再有奇怪的副作用
  • 按预期使用 ActiveSupport::Notifications
  • ActiveSupport::Notifications::Event wrapper gives you nice extras like #duration
  • 轻松检查是否有其他事件被触发
  • 仅查看与名称匹配的事件的能力 - 使用 ActiveSupport::Notifications.subscribe(/asd/) 对事件名称进行部分匹配
  • 更好的可读性 - 检查事件数组更具可读性

模拟的缺点

  • 更多代码
  • 改变 @events 数组
  • 如果您在 teardown
  • 上不清除 @events,则测试之间可能存在依赖关系