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

Clear let/subject memoization when referenced in before(:all). #811

Merged
merged 1 commit into from Mar 2, 2013
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 16 additions & 1 deletion lib/rspec/core/example_group.rb
Expand Up @@ -299,11 +299,26 @@ def self.before_all_ivars
# @private
def self.store_before_all_ivars(example_group_instance)
return if example_group_instance.instance_variables.empty?

example_group_instance.instance_variables.each { |ivar|
before_all_ivars[ivar] = example_group_instance.instance_variable_get(ivar)
value = example_group_instance.instance_variable_get(ivar)

if ivar.to_sym == :@__memoized
warn_about_unsound_let_usage(value)
else
before_all_ivars[ivar] = value
end
}
end

def self.warn_about_unsound_let_usage(let_hash)
called_lets = let_hash.keys.map { |l| "`#{l}`" }.join(' and ')

::RSpec.warn_deprecation "WARNING: let declaration(s) #{called_lets} referenced " +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see this reference the line on which the before(:all) hook is declared. Also, not sure if this is already the case, but once per group (rather than once per example) would be nice.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback. I wanted to get a backtrace line in this message but couldn't think of a simple way to do that. Thinking about it some more, I've come up with a different approach that'll make that easy. I'll try to put up a PR later today with that fix.

"in a `before(:all)` hook. The memoized value(s) will be discarded since this " +
"is outside the scope of an example."
end

# @private
def self.assign_before_all_ivars(ivars, example_group_instance)
ivars.each { |ivar, val| example_group_instance.instance_variable_set(ivar, val) }
Expand Down
83 changes: 83 additions & 0 deletions spec/rspec/core/memoized_helpers_spec.rb
Expand Up @@ -120,6 +120,47 @@ def subject_value_for(describe_arg, &block)

expect(subject_value).to eq([4, 5, 6, :override])
end

context 'when referenced in a `before(:all)` hook' do
before do
expect(::RSpec).to respond_to(:warn_deprecation)
::RSpec.stub(:warn_deprecation)
end

def define_and_run_group
final_subject_value_in_before_all = subject_value_in_example = nil

ExampleGroup.describe do
subject { [1, 2] }

before(:all) do
subject << 3
final_subject_value_in_before_all = subject
end

example { subject_value_in_example = subject }
end.run

return final_subject_value_in_before_all, subject_value_in_example
end

it 'memoizes the value within the before(:all) hook' do
value, _ = define_and_run_group
expect(value).to eq([1, 2, 3])
end

it 'clears the memoization before the first example' do
_, value = define_and_run_group
expect(value).to eq([1, 2])
end

it 'prints a warning since `subject` declarations are not intended to be used in :all hooks' do
::RSpec.should_receive(:warn_deprecation).
with(/let declaration\(s\) `subject` referenced in a `before\(:all\)`/)

define_and_run_group
end
end
end

describe "with a name" do
Expand Down Expand Up @@ -466,6 +507,47 @@ def count
expect(value).to eq(:late_exit)
end
end

context 'when referenced in a `before(:all)` hook' do
before do
expect(::RSpec).to respond_to(:warn_deprecation)
::RSpec.stub(:warn_deprecation)
end

def define_and_run_group
final_list_value_in_before_all = list_value_in_example = nil

ExampleGroup.describe do
let(:list) { [1, 2] }

before(:all) do
list << 3
final_list_value_in_before_all = list
end

example { list_value_in_example = list }
end.run

return final_list_value_in_before_all, list_value_in_example
end

it 'memoizes the value within the before(:all) hook' do
value, _ = define_and_run_group
expect(value).to eq([1, 2, 3])
end

it 'clears the memoization before the first example' do
_, value = define_and_run_group
expect(value).to eq([1, 2])
end

it 'prints a warning since `let` declarations are not intended to be used in :all hooks' do
::RSpec.should_receive(:warn_deprecation).
with(/let declaration\(s\) `list` referenced in a `before\(:all\)`/)

define_and_run_group
end
end
end

describe "#let!" do
Expand Down Expand Up @@ -510,3 +592,4 @@ def count
end
end
end