Last active
November 29, 2018 01:34
-
-
Save ches/4993289 to your computer and use it in GitHub Desktop.
Example of testing Rails observers in isolation for cross-cutting concerns
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'spec_helper' | |
# Bustle is a pubsub system used for activity streams: | |
# https://github.com/fredwu/bustle | |
# | |
# Here when a person follows another (or a discussion, for instance), the observer wires | |
# up pubsub between them for future activity notifications. The Follow model knows nothing | |
# about the implementation choices for the pubsub system. | |
describe FollowObserver do | |
subject { FollowObserver.instance } | |
let(:follow) { Follow.make!(:user) } | |
let(:follower) { follow.user } | |
let(:followable) { follow.followable } | |
describe 'after_create' do | |
it 'subscribes follower to future activity from the followable' do | |
expect { subject.after_create(follow) }. | |
to subscribe(follower).to(followable) | |
end | |
it 'publishes followed activity for the followable' do | |
expect { subject.after_create(follow) }. | |
to publish_activity('followed').about(followable).by(follower) | |
end | |
it 'publishes followed by activity for the followable' do | |
expect { subject.after_create(follow) }. | |
to publish_activity('been_followed_by').about(follower).by(followable) | |
end | |
end | |
describe 'before_destroy' do | |
before(:each) do | |
Follow.observers.enable(:all) { follow } | |
end | |
it 'unsubscribes follower from future activity from the followable' do | |
publisher = Bustle::Publishers.get(followable) | |
subscriber = Bustle::Subscribers.get(follower) | |
expect { subject.before_destroy(follow) }. | |
to change { Bustle::Subscriptions.get(publisher, subscriber) } | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# I wish I had a better example at the moment without the distraction of the extra | |
# code for these custom matchers, but realize that they serve to make assertions | |
# succinct -- they have no bearing on on the setup. If concerns are not overlapping, | |
# such isolated observer specs shouldn't have a lot of pain with setting up model | |
# instance state. | |
RSpec::Matchers.define :publish_activity do |action| | |
chain :about do |resource| | |
@resource = resource | |
end | |
chain :by do |publisher_resource| | |
@publisher = Bustle::Publishers.get(publisher_resource) | |
end | |
match do |expect_proc| | |
expect_proc.call | |
conditions = { action: action } | |
conditions.merge!(publisher_id: @publisher.id) if @publisher | |
if @resource | |
conditions.merge!( | |
resource_id: @resource.id, | |
resource_class: @resource.class.name | |
) | |
end | |
Bustle::Activity.where(conditions).one? | |
end | |
end | |
RSpec::Matchers.define :subscribe do |subscriber_resource| | |
chain :to do |publisher_resource| | |
@publisher = Bustle::Publishers.add publisher_resource | |
end | |
match do |expect_proc| | |
expect_proc.call | |
subscriber = Bustle::Subscribers.add subscriber_resource | |
conditions = { subscriber_id: subscriber.id } | |
conditions.merge!(publisher_id: @publisher.id) if @publisher | |
Bustle::Subscription.where(conditions).one? | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
RSpec.configure do |config| | |
# Assure we're testing models in isolation from Observer behavior. Enable | |
# them explicitly in a block if you need to integration test an Observer -- | |
# see the documentation for {ActiveModel::ObserverArray}. | |
config.before do | |
ActiveRecord::Base.observers.disable :all | |
end | |
# Integration tests are full-stack, lack of isolation is by design. | |
config.before(type: :feature) do | |
ActiveRecord::Base.observers.enable :all | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment