Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add stubbing test helpers for the event bus #4214

Merged
merged 1 commit into from
Dec 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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