Как отследить только один вызов ActiveSupport::Notifications #instrument, а не все

Я делаю тест 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

Есть ли другой (лучший) способ?


person Caio Salgado    schedule 28.01.2019    source источник


Ответы (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
person John Gallagher    schedule 29.01.2019