Skip to content

Commit

Permalink
Merge pull request #4214 from solidusio/waiting-for-dev/events_stubs
Browse files Browse the repository at this point in the history
Add stubbing test helpers for the event bus
  • Loading branch information
waiting-for-dev authored Dec 7, 2021
2 parents 27c27d0 + e2a0ac9 commit 5de0fe5
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 0 deletions.
103 changes: 103 additions & 0 deletions core/lib/spree/testing_support/event_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# frozen_string_literal: true

require 'spree/event'

module Spree
module TestingSupport
# RSpec test helpers for the event bus
#
# If you want to use the methods defined in this module, include it in your
# specs:
#
# @example
# require 'rails_helper'
# require 'spree/testing_support/event_helpers'
#
# RSpec.describe MyClass do
# include Spree::TestingSupport::EventHelpers
# end
#
# or, globally, in your `spec_helper.rb`:
#
# @example
# require 'spree/testing_support/event_helpers'
#
# RSpec.configure do |config|
# config.include Spree::TestingSupport::EventHelpers
# end
module EventHelpers
extend RSpec::Matchers::DSL

# Stubs {Spree::Event}
#
# After you have called this method in an example, {Spree::Event} will no
# longer listen to any event for the duration of that example. All the
# method invocations on it will be spied but not performed.
#
# Internally, it stubs {Spree::Event} to a class spy of itself.
#
# After you call this method, probably you'll want to call some of the
# matchers defined in this module.
def stub_spree_events
stub_const('Spree::Event', class_spy(Spree::Event))
end

# @!method have_been_fired(event_name)
# Matcher to test that an event has been fired via {Spree::Event#fire}
#
# Before using this matcher, you need to call {#stub_spree_events}.
#
# Remember that the event listeners won't be performed.
#
# @example
# it 'fires foo event' do
# stub_spree_events
#
# Spree::Event.fire 'foo'
#
# expect('foo').to have_been_fired
# end
#
# It can be chain through `with` to match with the published payload:
#
# @example
# it 'fires foo event with the expected payload' do
# stub_spree_events
#
# Spree::Event.fire 'foo', bar: :baz, qux: :quux
#
# expect('foo').to have_been_fired.with(a_hash_including(bar: :baz))
# end
#
# @param [String, Symbol] event_name
matcher :have_been_fired do
chain :with, :payload

match do |expected_event|
expected_event = normalize_name(expected_event)
arguments = payload ? [expected_event, payload] : [expected_event, any_args]
expect(Spree::Event).to have_received(:fire).with(*arguments)
end

failure_message do |expected_event|
<<~MSG
expected #{expected_event.inspect} to have been fired.
Make sure that provided payload, if any, also matches.
MSG
end

def normalize_name(event_name)
if event_name.is_a?(String)
eq(event_name).or(eq(event_name.to_sym))
elsif event_name.is_a?(Symbol)
eq(event_name).or(eq(event_name.to_s))
else
raise ArgumentError, <<~MSG
"#{event_name.inspect} is not a valid event name. It must be a String or a Symbol."
MSG
end
end
end
end
end
end
110 changes: 110 additions & 0 deletions core/spec/lib/spree/core/testing_support/event_helpers_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# frozen_string_literal: true

require 'spec_helper'
require 'spree/testing_support/event_helpers'

RSpec.describe Spree::TestingSupport::EventHelpers do
include described_class

describe '#stub_spree_events' do
it 'creates a spy class from Spree::Event and assigns to itself' do
stub_spree_events

expect(Spree::Event.inspect).to include('ClassDouble')

Spree::Event.fire 'foo'

expect(Spree::Event).to have_received(:fire)
end
end

describe '#have_been_fired' do
it "matches when the event has been fired without payload and there's no expectation on it" do
stub_spree_events

Spree::Event.fire 'foo'

expect('foo').to have_been_fired
end

it "matches when the event has been fired with payload but there's no expectation on it" do
stub_spree_events

Spree::Event.fire 'foo', bar: :baz

expect('foo').to have_been_fired
end

it "matches when the event has been fired with payload and the expectation on it matches" do
stub_spree_events

Spree::Event.fire 'foo', bar: :baz

expect('foo').to have_been_fired.with(bar: :baz)
end

it "matches when fired as string and matched as string" do
stub_spree_events

Spree::Event.fire 'foo'

expect('foo').to have_been_fired
end

it "matches when fired as string but matched as symbol" do
stub_spree_events

Spree::Event.fire 'foo'

expect(:foo).to have_been_fired
end

it "matches when fired as symbol but matched as string" do
stub_spree_events

Spree::Event.fire :foo

expect('foo').to have_been_fired
end

it "matches when fired as symbol and matched as symbol" do
stub_spree_events

Spree::Event.fire :foo

expect(:foo).to have_been_fired
end

it "can match payload with an inner matcher" do
stub_spree_events

Spree::Event.fire 'foo', bar: :baz, tar: :tar

expect('foo').to have_been_fired.with(a_hash_including(bar: :baz))
end

it "doesn't match when the event hasn't been fired" do
stub_spree_events

expect {
expect('foo').to have_been_fired
}.to raise_error /expected "foo" to have been fired/
end

it "doesn't match when the event has been fired but the payload doesn't match" do
stub_spree_events

Spree::Event.fire 'foo', foo: :bar

expect {
expect('foo').to have_been_fired.with(bar: :baz)
}.to raise_error /Make sure that provided payload.*also matches/
end

it "raises when expected event is not a valid name" do
expect {
expect([]).to have_been_fired.with(bar: :baz)
}.to raise_error /not a valid event name/
end
end
end

0 comments on commit 5de0fe5

Please sign in to comment.