如何仅监视一次ActiveSupport :: Notifications #instrument,而不是全部监视

时间:2019-01-28 22:25:50

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

我正在进行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的电话。

我只能在嘲笑allow的那个之前加上另外一个:asd

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

还有另一种(更好)的方式吗?

1 个答案:

答案 0 :(得分: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,则测试之间可能存在依赖性