Reputation: 951
I encountered an issue with some Web UI Automation using RSpec and Selenium/Capybara/SitePrism. It's related to the combination of before :all
and before :each
clauses that occur in the code, and perhaps especially in my spec_helper
file.
Up until now, I've been running rspec against one spec file at a time. Each spec file required a spec_helper
file, which included the following:
spec_helper.rb:
RSpec.configure do |config|
config.before(:all) do
# 1) Code to configure WebDriver and launch Browser is here
end
end
The spec files themselves contain their own before
blocks for various reasons. Most of them contain something like the following:
test_a_spec.rb:
describe "Page A" do
before :all do
# 2) Log in to web site, maybe load the test page in question
end
it "does this thing" do
# 3) Test this thing
end
it "does that thing" do
# 4) Test that thing
end
end
This worked fine as long as I was running RSpec against individual spec files. When I tried tagging some of my examples and then running against the whole spec folder, I had a problem. The before :all
block in spec_helper.rb
didn't prepend itself to every file, as I thought it would, but instead ran once at the beginning. All the spec files after the first were expecting a clean browser to be launched by spec_helper
and to do the log in part themselves, but the browser wasn't clean and was already logged in, so that wasn't good.
Changing the before :all
in spec_helper.rb
to a before :each
seemed like the natural solution, so I did, and suddently my tests instantly failed with an error claiming that rack-test requires a rack application, but none was given
. This happened to some of my tests but not all, and through process of elimination I realized it was only tests that had their own before :all
blocks that were failing. It appears that the before :all
in the spec file is superceding the before :each
in the spec_helper
file. So it was trying to log in before it had launched a browser.
This vexes me. I am terribly vexed. I had sort of assumed two things:
I assumed before :each
was on equal footing with before :all
, and in fact imagine it like a before :all
plus more, in a sense. The idea that a before :all
would supercede a before :each
seems weird to me.
I assume all these before
blocks were subject to nesting in a reasonable way. That is, a before :all
block should fire once before all the things below it, which would mean firing within each iteration of a before :each
block that might contain the before :all
block. Also, if a before :each
contains some code and then a describe statement below it has a before :all
, the before :each
code should still fire before the before :all
code. Maybe it does do that, I'm just not sure at this point.
Questions:
1) What kind of behavior is my spec_helper
file actually producing? If I were to take that same behavior and put it into one of the spec files itself, for instance, what would that look like? Would the before :each
with the browser launch code wrap around all the code in the spec file in some sort of implicit describe
block? Does the before :each
get inserted into the outermost describe block, and therefore have to compete with the spec file's before :all
block?
2) I could "solve" this by abandoning all before :all
blocks in my tests, but I like the flexibility of the way things are, and since this is UI Automation and speed is a factor, I don't really want to be opening a new browser and logging in for each describe, even though I'm aware that this would be ideal for keeping every test separated from the others. Do I have to/want to do just that?
Thanks!
Upvotes: 2
Views: 2968
Reputation: 377
The RSpec documentation (http://rspec.info/documentation/3.3/rspec-core/RSpec/Core/Hooks.html#before-instance_method) provides some very useful information about how before blocks interact and the order in which they are run, but they do recommend avoiding before(:context) [aka before(:all)].
FYI, note that the after blocks are run in the reverse order fin which before blocks are.
Upvotes: 0
Reputation: 29399
To address the part of your question not already covered by https://stackoverflow.com/a/22489263/1008891, the require
method will not reload any file already loaded previously, as described in https://stackoverflow.com/a/22489263/1008891. This contrasts with the load
method, which acts like a "copy/paste" of the file contents wherever it is used.
I'm not saying that if you changed all your requires
to loads
that you'd get the same result as running the individual files, as there may be other global state that affects the behavior you're seeing.
Upvotes: 2