Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Driver swapping on RSpec 2 #187

Closed
wants to merge 2 commits into from

5 participants

@jeffkreeftmeijer

I built a really simple extension to Capybara called Swinger to allow quick driver swapping using RSpec's new metadata feature last week. You can read more about it in the article.

I wanted to just get it out there to use it myself and see if it needs any improvement before turning it into a patch for Capybara, so I released it as a gem first. After some small fixes, I turned it into a patch. What do you think? :)

@jnicklas
Owner

Is monkey patching RSpec the only/right way of achieving this? Seems a bit potentially dangerous.

@jeffkreeftmeijer

I wanted to use RSpec's around filter to do something like this at first:

RSpec.configure do |c|
  c.around(:each) do |example|
    puts "set driver"
    example.run
    puts "reset driver"
  end
end

But since the example above is a proc and not an actual RSpec::Core::Example or RSpec::Core::ExampleGroup, I couldn't find a way to get the metadata to get the :driver.

Extending RSpec::Core::Example#run did allow me to get the metadata.

I'd love to know if anyone has a better way to solve this, though. :)

@alloy

First let me put a disclaimer in place, I do not use RSpec, but I assume that whether or not it provides a ‘around-run’ hook has been investigated and the answer is ‘no’.

I personally think this kind of monkey-patching is safe enough, however I do understand your concern. This is what I would do:

  • accept the monkey-patch, for now
  • create a patch for RSpec adding a way to add a ‘around-run’ hook, which can then be discussed with the actual use case that’s in Capybara
  • remove monkey-patch from Capybara

Obviously the work should probably be done by Jeff, as he is the one using it :)

@alloy

Ok, I tried to fix the list formatting, but I fail… :-/

@jnicklas
Owner

Someone posted this on twitter: https://gist.github.com/669072

Seems like a much more elegant solution, unless I'm missing something? We could use this to enable both:

 it "foo", :driver => :akephalos
 it "foo", :js => true

From what I can see.

@alloy

If Swinger also only allows you to set a driver for a complete group, instead of per-example, then yes.

In your example you are using `it "foo", :driver => :akephalos', but that does not work if you add before and after hooks. I.e. they would run for each example in a group.

@jnicklas
Owner

alloy: ye, but unless the example was tagged with the appropriate metadata they wouldn't actually do anything, no?

@alloy

Ah yeah, you’re right, I looked to quick.

However, now that David has already picked it up, it’s probably better to do it that way, even if it internally would use the example from the gist. https://github.com/rspec/rspec-core/issues/issue/221

@jeffkreeftmeijer

@jnicklas I'm not sure that will work, since I don't think it's able to access the example.metadata, which is the why Swinger can't use the around block: #187 (comment)

@alloy Swinger allows you to set the driver per example and per group. :)

I'll send in a new pull request when RSpec has metadata in its around blocks: https://github.com/rspec/rspec-core/issues#issue/221 :)

@unders

Add RSpec support in Capybara itself, closed by aa46894

Just the basics of including Capybara and setting
up some metadata to switch between drivers.

@jnicklas
Owner

So I just added some basic rspec support. It's pretty simple and doesn't freedom patch RSpec in any way. I'm pretty sure this should work as you intended. I've also merged and pushed the addition of using_driver, since it seemed just generally useful (though it isn't actually used by the rspec driver switching).

P.S.: The name on the commit is wrong, I forgot to switch my git info, I am the author.

@cavalle

Good one. The only problem I see is that capybara/rspec will include capybara for any spec (i.e. in Rails, any model, controller or helper spec will include Capybara) which is not completely polite and might cause issues. In Steak, scenarios have the type metadata value set to acceptance, so you can include modules and configure hooks only for that type of specs.

Some discussion about this issue in Steak's mailing list: http://groups.google.com/group/steakrb/browse_thread/thread/28fa2b843e0239fd

@jnicklas
Owner

Ahh, that seems like a good idea. Will have to fix that!

@mcmire mcmire referenced this pull request from a commit
Anders Törnqvist Add RSpec support in Capybara itself, closes #187
Just the basics of including Capybara and setting
up some metadata to switch between drivers.
aa46894
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
11 lib/capybara/dsl.rb
@@ -41,6 +41,17 @@ def use_default_driver
##
#
+ # Yield a block using a specific driver
+ #
+ def using_driver(driver)
+ Capybara.current_driver = driver
+ yield
+ ensure
+ Capybara.use_default_driver
+ end
+
+ ##
+ #
# The current Capybara::Session base on what is set as Capybara.app and Capybara.current_driver
#
# @return [Capybara::Session] The currently used session
View
12 lib/capybara/rspec.rb
@@ -0,0 +1,12 @@
+if defined? RSpec::Core::Example
+ class RSpec::Core::Example
+
+ alias_method :__run_before_swinger, :run
+ private :__run_before_swinger
+
+ def run(*args)
+ Capybara.using_driver(metadata[:driver]) { __run_before_swinger(*args) }
+ end
+
+ end
+end
View
26 spec/dsl_spec.rb
@@ -56,6 +56,32 @@
end
end
+ describe '#using_driver' do
+ before do
+ Capybara.current_driver.should_not == :selenium
+ end
+
+ it 'should set the driver using Capybara.current_driver=' do
+ driver = nil
+ Capybara.using_driver(:selenium) { driver = Capybara.current_driver }
+ driver.should == :selenium
+ end
+
+ it 'should reset the driver using Capybara.use_default_driver, even if an exception occurs' do
+ begin
+ Capybara.using_driver(:selenium) { raise "ohnoes!" }
+ rescue Exception
+ end
+ Capybara.current_driver.should == Capybara.default_driver
+ end
+
+ it 'should yield the passed block' do
+ called = false
+ Capybara.using_driver(:selenium) { called = true }
+ called.should == true
+ end
+ end
+
describe '#app' do
it "should be changeable" do
Capybara.app = "foobar"
View
21 spec/rspec_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+require 'capybara/dsl'
+require 'capybara/rspec'
+
+describe RSpec::Core::Example do
+
+ before do
+ @group = RSpec::Core::ExampleGroup.describe
+ end
+
+ it 'should call Capybara.using_driver' do
+ Capybara.should_receive(:using_driver).with(:selenium)
+ @group.example("does something", {:driver => :selenium}).run
+ end
+
+ it "does not show the original aliased method" do
+ methods = @group.example("without public aliased method").methods
+ methods.should_not include('__run_before_swinger')
+ end
+
+end
Something went wrong with that request. Please try again.