【问题标题】:How to spy just one call of ActiveSupport::Notifications #instrument, not all of them如何只监视一次 ActiveSupport::Notifications #instrument 调用,而不是全部调用
【发布时间】:2019-01-28 22:25:49
【问题描述】:

我正在做一个 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 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

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

【问题讨论】:

    标签: ruby rspec factory-bot activesupport rspec-mocks


    【解决方案1】:

    一般来说,我倾向于避免嘲弄。

    我遇到了类似的问题,我是这样解决的:

      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 包装器为您提供了不错的附加功能,例如 #duration
    • 轻松检查触发的其他事件
    • 仅查看与名称匹配的事件的能力 - 使用 ActiveSupport::Notifications.subscribe(/asd/) 对事件名称进行部分匹配
    • 更好的可读性 - 检查事件数组更具可读性

    模拟的缺点

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-11
      • 1970-01-01
      • 1970-01-01
      • 2023-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多