|
# 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 |