Skip to content

Instantly share code, notes, and snippets.

@dnagir
Last active August 29, 2015 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dnagir/c47aba850d5a3adf931d to your computer and use it in GitHub Desktop.
Save dnagir/c47aba850d5a3adf931d to your computer and use it in GitHub Desktop.
# spec/support/shared_subject.rb
module RSpecSharedSubjectMacros
# Usage:
# 1. Replace 'subject' with 'shared_subject' to share the first result acroos the whole context
# 2. (optional) replace 'before(:each)' blocks with 'shared_setup'
# 3. Done. The subject will be shared within one, single context (nested will be re-setup again)
#
# DANGER NOTE: Make sure you accept the disadvantages of this, such as:
# - there's a class variable that stores the shared data
#
# - that variable is set only once, when the first spec is run within the context
#
# - that variable is set withing normal spec run (so all the setup/teardown should work fine)
#
# - all the future tests will only share the subject, any 'let'-s or others will still be re-setup
#
# - some assertions cannot be used on shared subjects (such as 'should render_template')
# because the actual action has not been executed and only the result has been provided
#
# - set-up in the spec against subject is basically ignored (subject isn't render in the following tests):
# it 'should be have NRAS' do
# proeprty.nras = true # does not matter. This should be in 'before' block
# should have_content 'NRAS Dwelling'
# end
#
# - database assertions (unless subject will return snapshot of data in custom format)
# are not possible because the database is normally reset between the specs
#
#
# If there are leak-issues then the suite can be converted back to normal by:
# 1. Replace shared_subject with subject
# 2. Replace shared_setup with before blocks
def shared_subject(*args, &block)
class_attribute :shared_subject_cache
# Reset the subject for the whole context
#
# ALL run in the context of an ExampleGroup instance
before(:all) { self.class.shared_subject_cache = { } }
after(:all) { self.class.shared_subject_cache = nil }
subject(*args) { RSpecSharedSubjectMacros.evaluate_shared_subject(self, &block) }
# Executed if the shared subject hasn't been cached yet (hopefully only once)
def shared_setup(&block)
before(:each) do
if !self.class.shared_subject_cache[self.class]
# puts "*** Setup on #{self.class.inspect} ***"
instance_eval(&block)
end
end
end
end
private
def self.evaluate_shared_subject(me, &block)
me.class.shared_subject_cache[me.class] ||= eval_within_example(me, &block)
end
def self.eval_within_example(me, &block)
# puts "*** <-- subject eval --> ***"
me.instance_eval(&block)
end
end
RSpec.configure do |config|
config.extend(RSpecSharedSubjectMacros)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment