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 2010-03-06 18:19
on 2010-03-06 18:51
On Sat, Mar 6, 2010 at 11:09 AM, Jarmo Pertman <jarmo.p@gmail.com> 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.
on 2010-03-06 19:44
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 2010-03-07 14:15
On Sat, Mar 6, 2010 at 12:33 PM, Jarmo Pertman <jarmo.p@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.
on 2010-03-07 14:53
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 2010-03-07 19:19
On Sun, Mar 7, 2010 at 7:47 AM, Jarmo Pertman <jarmo.p@gmail.com> 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
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.