[rspec-users] how to pass objects from spec to formatter from before :all block?

34 views
Skip to first unread message

Jarmo Pertman

unread,
Mar 6, 2010, 12:09:32 PM3/6/10
to rspec...@rubyforge.org
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
_______________________________________________
rspec-users mailing list
rspec...@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

David Chelimsky

unread,
Mar 6, 2010, 12:28:59 PM3/6/10
to rspec-users

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.

Jarmo Pertman

unread,
Mar 6, 2010, 1:33:20 PM3/6/10
to rspec...@rubyforge.org
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 Mar 6, 7:28 pm, David Chelimsky <dchelim...@gmail.com> wrote:

> rspec-us...@rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users

David Chelimsky

unread,
Mar 7, 2010, 8:12:09 AM3/7/10
to rspec-users
On Sat, Mar 6, 2010 at 12:33 PM, Jarmo Pertman <jar...@gmail.com> 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.

Jarmo Pertman

unread,
Mar 7, 2010, 8:47:48 AM3/7/10
to rspec...@rubyforge.org
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

On Mar 7, 3:12 pm, David Chelimsky <dchelim...@gmail.com> wrote:

> > rspec-us...@rubyforge.org

David Chelimsky

unread,
Mar 7, 2010, 1:10:30 PM3/7/10
to rspec-users

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

Reply all
Reply to author
Forward
0 new messages