Skip to content

Test only selected listeners with RSpec

Mateusz Wolsza edited this page Aug 27, 2019 · 5 revisions

How to prevent listener from being called in every test but make it active for selected ones.

EXAMPLE

publisher (app/models/order.rb)

    class Order < ActiveRecord::Base
      include Wisper::Publisher
    
      after_save do
        publish(:order_after_save, self)
      end
    
    end

listener/observer (app/observers/mailer_order_observer.rb)

    class MailerOrderObserver
    
      def order_after_save order
        # some actions after saving the order
        # which we don't want to execute during all tests just in selected blocks
        # e.g. sending an email
      end
    end

subscription (config/initializers/wisper.rb)

    Rails.application.config.to_prepare do
      Wisper.clear if Rails.env.development? || Rails.env.test?
      # add observers
      Wisper.subscribe(MailerOrderObserver.new)
    end

spec/spec_helper.rb

    RSpec.configure do |config|
    
      config.before(:suite) do
        # unsubscribe observer before tests
        to_remove = Wisper::GlobalListeners.listeners.select do |l|
          l.instance_of?(MailerOrderObserver) # get listeners of selected class
        end
        to_remove.each do |remove|
          Wisper::GlobalListeners.unsubscribe(remove)
        end
      end
    end

test (spec/observers/mailer_order_observer_spec.rb)

    require 'spec_helper'
    
    describe MailerOrderObserver, type: :model do
      # ! prevents lazy loading
      let! (:order) { create(:order) }
      let (:listener) { MailerOrderObserver.new }

      describe '.order_after_save' do
        # thanks to that create(:order) will not trigger listener callback method
        before { Wisper.subscribe(listener) }
        before { Wisper.unsubscribe(listener) } # not available even in 2.0.0rc1 (use master branch head?)
    
        it 'should send email' do
          expect(order.save).to eq(true)
        end

        it 'should send email another test' do
          expect(order.save).to eq(true)
        end
      end
    end

This way MailerOrderObserver receives notifications from publisher only when Wisper.subscribe... is executed before the test.

Note that it is convenient to use let! (with exclamation mark) in order to instantiate order before subscribing to the Wisper. This way only one publisher notification is received during test. Using let instead will cause two notifications: one after create... and one caused by order.save.