If let (: thing) is supposed to be written as an example, why am I seeing the value when I run the second example?
We have a template like this in the spec:
describe Thing do
sanity0 = '../test-data/sanity.mbox'
let(:sanity1) {
File.join(@scratch, 'sanity1.mbox').tap { |f|
FileUtils.cp sanity0, f
}
}
after(:example) do
FileUtils.rm sanity1
end
before(:example) do
# does stuff using sanity1
sanity1
end
uses_case(proc { [sanity0, sanity1] })
describe '#method1' do
it 'does something' do
# ...
end
it 'does another thing' do
# ...
end
end
# ... more methods ...
end
So, sanity1
used from before(:example)
, initialized and then eventually dumped into after(:example)
.
The helper uses_case
contains a shorthand for a before / after pair that creates more data on disk and then deletes it. The list of files passed to is usually passed as a proc, because otherwise you will be calling let
things from the wrong place.
def uses_case(files)
before do
# instance_exec is here because RSpec whines if you call the proc
# directly instead of in the context of the example.
files = instance_exec(&files) if files.is_a?(Proc)
# Uses files to initialise case
end
after do
# cleanup stuff
end
end
The problem is, when the second example is executed, it fails:
[java] Java::JavaIo::FileNotFoundException:
[java] File does not exist: /var/folders/d5/p7yfxkhx4673nd7z7lz_t36c0000gn/T/scratch20170525-83837-1r7t2g/sanity1.mbox
So, it somehow entered the second test as a value from the first test ... but the cleanup logic was run after that first test, so the file is now gone.
If I insert the output lines at the appropriate points:
[java] Thing
[java] #method1
[java] let(:sanity1)
[java] cleanup(:sanity1)
[java] does something
[java] does another thing (FAILED - 1)
[java] #method2
[java] does something (FAILED - 2)
[java] does another thing (FAILED - 3)
It's so clear that he only really calls it once at the context level.
How can it be? If I write a stand-alone case that only has examples and no other structure, it behaves in a completely opposite way, calling both blocks for each individual example.
Edit: Further shortening, I managed to get it autonomous:
require 'rspec'
def uses_case(files)
before do
files = instance_exec(&files) if files.is_a?(Proc)
end
end
describe 'something' do
let(:in_describe) { $stderr.puts('let(:in_describe)'); 'd' }
uses_case(proc { [in_describe] })
context 'when context 1' do
it 'does something' do
$stderr.puts('in example 1-1')
end
it 'does something else' do
$stderr.puts('in example 1-2')
end
end
end
Result:
$ rspec test_spec.rb
let(:in_describe)
in example 1-1
.in example 1-2
.
If I add more diagnostic data to it, it gets weirder:
--- in before for uses_case ---
self = #<RSpec::ExampleGroups::Something::WhenContext1:0x1722011b>
__memoized = #<RSpec::Core::MemoizedHelpers::ThreadsafeMemoized:0x5b3f61ff>
contents: {}
--- in let(:in_describe) block ---
self = #<RSpec::ExampleGroups::Something::WhenContext1:0x1722011b>
__memoized = #<RSpec::Core::MemoizedHelpers::ThreadsafeMemoized:0x5b3f61ff>
contents: {}
after instance_exec
__memoized = #<RSpec::Core::MemoizedHelpers::ThreadsafeMemoized:0x5b3f61ff>
contents: {:in_describe=>"d"}
in example 1-1
.--- in before for uses_case ---
self = #<RSpec::ExampleGroups::Something::WhenContext1:0x45a4b042>
__memoized = #<RSpec::Core::MemoizedHelpers::ThreadsafeMemoized:0x327af41b>
contents: {}
after instance_exec
__memoized = #<RSpec::Core::MemoizedHelpers::ThreadsafeMemoized:0x327af41b>
contents: {}
in example 1-2
.
It looks like this, the second time, it is a new hash, and the hash is empty, but the block is let
not called.
source to share
No one has answered this question yet
Check out similar questions: