Skip to content

Commit

Permalink
Merge 9c06532 into c4e68f3
Browse files Browse the repository at this point in the history
  • Loading branch information
RobinDaugherty committed May 3, 2015
2 parents c4e68f3 + 9c06532 commit c98917a
Show file tree
Hide file tree
Showing 3 changed files with 285 additions and 2 deletions.
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,11 +255,45 @@ report_creator.subscribe(MailResponder.new, on: :create_report_failed,
You could also alias the method within your listener, as such
`alias successful create_report_successful`.

## RSpec
## Testing

### Test harness

Wisper allows you to dynamically configure the testing harness with the following methods:

``` ruby
require 'wisper/testing'
Wisper::Testing.disable! # this is the default, no change in Wisper functionality
Wisper::Testing.fake! # in this mode, events broadcasted are not delivered to listeners
```

Each of the above methods also accepts a block. An example:

``` ruby
require 'wisper/testing'
Wisper::Testing.fake!

# Some tests that do not require wisper events to be received

Wisper::Testing.disable! do
# Some other tests that rely on Wisper
end

# Here we're back to "fake" mode again.
```

To query the current state, use the following methods:

``` ruby
Wisper::Testing.fake?
Wisper::Testing.disabled?
```

### RSpec

Please see [wisper-rspec](https://github.com/krisleech/wisper-rspec).

## Clearing Global Listeners
### Clearing Global Listeners

If you use global listeners in non-feature tests you _might_ want to clear them
in a hook to prevent global subscriptions persisting between tests.
Expand Down
140 changes: 140 additions & 0 deletions lib/wisper/testing.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
require 'wisper'

module Wisper
class Testing
class << self
attr_accessor :__test_mode

def __set_test_mode(mode, &block)
if block
current_mode = self.__test_mode
begin
self.__test_mode = mode
block.call
ensure
self.__test_mode = current_mode
end
else
self.__test_mode = mode
end
end

def disable!(&block)
__set_test_mode(:disable, &block)
end

def fake!(&block)
__set_test_mode(:fake, &block)
end

def enabled?
self.__test_mode != :disable
end

def disabled?
self.__test_mode == :disable
end

def fake?
self.__test_mode == :fake
end

attr_accessor :__patch_state

# Performs any patching necessary for Wisper::Testing to function.
def patch!
return if patched?
Wisper::Publisher.wisper_testing_patch!
self.__patch_state = :patched
end

# Restores Wisper and related objects to their original state before {#patch} was called.
def unpatch!
return unless patched?
Wisper::Publisher.wisper_testing_unpatch!
self.__patch_state = :unpatched
end

def patched?
self.__patch_state == :patched
end
end
end

module Publisher
# If testing is in "fake" mode, returns `true` as if broadcasting worked. If not in "fake"
# mode, broadcasts the event using the original `broadcast` method.
# This allows tests to determine whether an event was broadcast, and optionally skip the
# delivery of the event.
def broadcast_testing(event, *args)
Publisher.record_testing_event(event, *args)
if Wisper::Testing.fake?
true
else
broadcast_real(event, *args)
end
end

def self.wisper_testing_patch!
alias_method :broadcast_real, :broadcast
alias_method :broadcast, :broadcast_testing
alias_method :publish, :broadcast_testing
private :broadcast, :publish
end

def self.wisper_testing_unpatch!
alias_method :broadcast, :broadcast_real
alias_method :publish, :broadcast_real
private :broadcast, :publish
end

# Allows tests to ask a Wisper publisher whether a listener has been subscribed to events
# from it.
def wisper_subscribed_locally?(listener)
local_registrations.any? { |registration| registration.listener == listener }
end

class << self
attr_reader :testing_event_recorder

def record_testing_event(event, *args)
testing_event_recorder.send(event, *args) if testing_event_recorder
end

# Allows tests to run a block that records all events sent during the block.
# `event_recorder` will have the event sent to it as if it were a listener that
# is subscribed to any of the events broadcast during this method's run.
def with_testing_event_recorder(event_recorder)
@testing_event_recorder = event_recorder
begin
yield
ensure
@testing_event_recorder = nil
end
end
end
end

class << self
# List of all registrations that exist in Wisper.
def registrations
GlobalListeners.registrations + TemporaryListeners.registrations
end

# Determines whether an object is registered as a listener within Wisper.
def subscribed?(listener)
registrations.any? { |reg| reg.listener == listener }
end

# Determines whether an object is registered as a listener for a specific publisher object.
def subscribed_to_publisher?(listener, publisher_class)
registrations.any? { |reg|
reg.listener == listener && reg.allowed_classes.include?(publisher_class.to_s)
}
end
end
end

unless ENV['SKIP_WISPER_TESTING_PATCH']
Wisper::Testing.patch!
end
109 changes: 109 additions & 0 deletions spec/lib/testing_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
ENV['SKIP_WISPER_TESTING_PATCH'] = 'true'
require 'wisper/testing'

describe 'Wisper::Testing' do
let(:our_publisher_class) { publisher_class }
let(:publisher) { our_publisher_class.new }
let(:listener) { double('listener', event_name: true) }

context 'after patching and unpatching' do
before do
Wisper::Testing.patch!
Wisper::Testing.unpatch!
Wisper::Testing.fake!
end
after do
Wisper::Testing.disable!
end
it 'uses the original broadcast method' do
publisher.subscribe(listener)
publisher.send(:broadcast, 'event_name', :arg1, :arg2)
expect(listener).to have_received(:event_name).with(:arg1, :arg2)
end
end

context 'while Wisper is patched' do
before do
Wisper::Testing.patch!
end
after do
Wisper::Testing.unpatch!
end

describe 'Wisper::Publisher.wisper_subscribed_locally?' do
subject { publisher.wisper_subscribed_locally?(listener) }

context 'when the listener is not registered' do
it { is_expected.to be_falsey }
end
context 'when the listener is registered' do
before do
publisher.subscribe(listener)
end
it { is_expected.to be_truthy }
end
end

describe 'Wisper.subscribed?' do
subject { Wisper.subscribed?(listener) }

context 'when the listener is not registered' do
it { is_expected.to be_falsey }
end
context 'when the listener is registered' do
before do
Wisper.subscribe(listener)
end
it { is_expected.to be_truthy }
end
end

describe 'Wisper.subscribed_to_publisher?' do
subject { Wisper.subscribed_to_publisher?(listener, our_publisher_class) }

context 'when the listener is not registered' do
it { is_expected.to be_falsey }
end
context 'when the listener is registered' do
before do
our_publisher_class.subscribe(listener)
end
it { is_expected.to be_truthy }
end
end

describe 'Wisper.with_testing_event_recorder' do
let(:event_recorder) { double('event_recorder', event_name: true) }
before do
publisher.subscribe(listener)
end
subject {
Wisper::Publisher.with_testing_event_recorder(event_recorder) do
publisher.send(:broadcast, 'event_name', :arg1, :arg2)
end
}
it 'records the event' do
expect(event_recorder).to receive(:event_name).with(:arg1, :arg2)
subject
end
context 'with Wisper::Testing.disabled! (the default)' do
it 'publishes the event' do
expect(listener).to receive(:event_name).with(:arg1, :arg2)
subject
end
end
context 'with Wisper::Testing.fake!' do
before do
Wisper::Testing.fake!
end
after do
Wisper::Testing.disable!
end
it 'does not publish the event' do
expect(listener).not_to receive(:event_name).with(:arg1, :arg2)
subject
end
end
end
end
end

0 comments on commit c98917a

Please sign in to comment.