How to pass objects from spec to formatter from before :all block?

Hello.

I need to pass something from before :all to formatter. I know that i
could use options hash from spec and then get the value back in
formatter, but it doesn’t work when i’m doing it from before :all.

So, this work:

in spec

before :each do
options[:something] = 1
end

in formatter

def example_failed(example, counter, failure)
puts example.options[:something] # outputs 1
super
end

But if i try to do the same thing from before :all, then it doesn’t
work.

So i looked into the source of RSpec and in example/example_methods.rb
in method set_instance_variables_from_hash there is a line which
ignores some instance variables among with others @_proxy, which has
this option hash initialized in before :all and that’s why it’s not
getting into the formatter - in other words it is just dropped.

Is this a bug or expected behaviour? If it’s expected then what’s the
reason and how could i get the desired results of passing something to
formatter from before :all?

Can’t we just pass this options hash along?

Jarmo

On Sat, Mar 6, 2010 at 11:09 AM, Jarmo P. [email protected]
wrote:

options[:something] = 1

Can’t we just pass this options hash along?
It was never intended that you would set values on options from inside
an example and access them in a formatter. You’ve happened on
something that just happens to work because we’re using a standard
Ruby data structure.

The reason it won’t work for before(:all) is that before(:all) gets
run before any of the examples are run - so it doesn’t have access to
the options hash, which is created per example in rspec-1.

What sort of information are you trying to get to your formatter?
Maybe there is a different way to do it.

Since i’m using Watir then i usually open up the browser in
before :all block like this:
before :all do
@browser = Watir::Browser.new
end

and then in the formatter I’m saving html of the browser - thus
needing to access the browser object.

I could solve it currently by using before :each block as shown in my
first post or a global variable (not nice at all), but i thought that
it would be more logical to set it into options in before :all once.

Also, if using before :each solution and something fails in
before :all, then formatter doesn’t know currently anything about the
browser object and cannot save the html or anything. It’s little bit
bad, since let’s say if i do also some “setup actions” in before :all
before the actual tests and what if something fails there?

But what would happen if example_group options would be merged with
example options?

Any other solutions?

Jarmo

On Sat, Mar 6, 2010 at 12:33 PM, Jarmo P. [email protected]
wrote:

Since i’m using Watir
then i usually open up the browser in
before :all block like this:
before :all do
@browser = Watir::Browser.new
end

and then in the formatter I’m saving html of the browser - thus
needing to access the browser object.

You should have access to it from within each example. This would be a
bit noisier, but you could do this:

before(:all) do
@browser = Watir::Browser.new
end
before(:each) do
options[:browser] = @browser
end

Even though that’s noisier, you could easily encapsulate that in a
method:

def with_new_browser
before(:all) do
@browser = Watir::Browser.new
end
before(:each) do
options[:browser] = @browser
end
yield
end

describe “the home page” do
with_new_browser do
it “looks awesome” do
# …
end
it “takes you where you want to go” do
# …
end
end
end

I could solve it currently by using before :each block as shown in my
first post or a global variable (not nice at all), but i thought that
it would be more logical to set it into options in before :all once.

Also, if using before :each solution and something fails in
before :all, then formatter doesn’t know currently anything about the
browser object and cannot save the html or anything. It’s little bit
bad, since let’s say if i do also some “setup actions” in before :all
before the actual tests and what if something fails there?

after(:each) and after(:all) are guaranteed to run regardless of what
happens in before(:each), before(:all), and the examples
themselves. So you could deliver the browser there:

def with_new_browser
before(:all) do
@browser = Watir::Browser.new
end
yield
after(:each) do
options[:browser] = @browser
end
end

But what would happen if example_group options would be merged with
example options?

In rspec-1 that would be problematic, but this is already supported in
rspec-2 out of the box, so you may want to give rspec-2 a try.

On Sun, Mar 7, 2010 at 7:47 AM, Jarmo P. [email protected] wrote:

end
p “all”
correct browser object nevertheless. It will be messier of course, but
with_new_browser.

It would be great of course if i wouldn’t have to fill options two
times.

Again, there was never an intent to be able to modify the options hash
at all from within an example. The fact that you can in
before/after(:each) is an unintended by-product of the implementation,
and making it available in before(:all) would require some
re-architecting that is simply not going to happen in the context of
rspec-1 now that we’re actively working on rspec-2.

Rspec-2 does give you access to a metadata hash, which you can access
through a formal API from an example, before(:each) or after(:each):

describe “something” do
it “does something” do
running_example.metadata[:description].should eq(“does something”)
end
end

At the moment you can’t access it in before(:all) because there is no
running_example yet, but we should be able to provide access to it
through a new API. I’ll look into that in rspec-2. I’ve added an issue
for it: http://github.com/rspec/rspec-core/issues/issue/6

In the mean time, for better or worse, I’d stick with a global because
it works right now. That’s how I always did this sort of thing when I
was using rspec to drive WATIR or Selenium.

HTH,
David

Thank you for your thoughts. Unfortunately it seems that after :each
is not run if before :all fails…

And after :all is ran after example_failed in formatter. Consider spec
like this:

describe “something” do
before :all do
p “before”
raise
end

it “does” do
p “it”
end

after :each do
p “each”
end

after :all do
p “all”
end
end

Output of it will be:
“before”
F"all"

In other words, I’d have to put @browser into options in before :all
AND in after :each. After :each is actually a good place, because then
if @browser object is changed within the it block, then formatter gets
correct browser object nevertheless. It will be messier of course, but
I thought that I’d use Spec::Runner.configure in spec_helper or
somewhere:
Spec::Runner.configure do |config|
config.before(:all) {@browser = options[:browser] =
Watir::Browser.new}
config.after(:each) {options[:browser] = @browser}
end

Does it make sense? In that way i wouldn’t have to write my specs by
using any custom way (like you demonstrated with your method
with_new_browser.

It would be great of course if i wouldn’t have to fill options two
times.

Jarmo

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs