I'm using cucumber + capybara for some web automation testing. I'd love to be able to wire up my own tag (something like @all_browsers before the scenario) and have it run against a list of web drivers I set (celerity, selenium on firefox, ie and chrome). I don't want to have to write the scenario 4 different times with 4 different tags out front. I've looked into trying to do this with a new driver I register via:
Capybara.register_driver :all_browsers do |app|
# What would even work in here? I don't think anything will.
end
And then following it up with:
Before('@all_browsers') do
# Same problem here.
end
But I'm not quite sure what to put in that Before method that might actually work.
I've tried using cucumber hooks, specifically:
Around('@all_browsers') do |scenario, block|
Capybara.current_driver = :selenium_firefox
block.call
Capybara.current_driver = :selenium_chrome
block.call
# etc
end
But this doesn't behave as I had hoped. It uses the same driver and runs the scenario twice with it.
Following along the hook lines, there's this from the cucumber documentation:You may also provide an AfterConfiguration hook that will be run after Cucumber has been configured. This hook will run only once; after support has been loaded but before features are loaded. You can use this hook to extend Cucumber, for example you could affect how features are loaded...
This may be a potential path to go down for this, but I've not managed to come up with anything that works here either.
I've looked into custom formatters, but they really only look like they do exactly that - format the output, not so much designate how the features are actually run.
I've looked into overriding cucumber's feature runner, but that doesn't look easy or friendly to do.
Help please? Anyone?
Cucumber can be executed in parallel using TestNG and Maven test execution plugins by setting the dataprovider parallel option to true. In TestNG the scenarios and rows in a scenario outline are executed in multiple threads. One can use either Maven Surefire or Failsafe plugin for executing the runners.
There is no support for creating a scenario that depends on another scenario in Cucumber-JVM.
You can choose to run a specific scenario using the file:line format, or you can pass in a file with a list of scenarios using @-notation. steps, tags, comments, description, data tables or doc strings.
So, I wound up rolling my own solution to this. Not sure if it was the best or most elegant approach, but I actually just wound up:
env.rb
env.rb
and then set the default driver for Capybara to the appropriate driver.Working like a charm and I think might have actually wound up better in the end than anything I was trying above, as within the Thor file I was able to add things like a benchmarking option, as well as whether or not to split the feature run up into multiple threads. Still curious if anyone else came up with a solution for this though.
cucumber.yaml:
Here, the all_features file just does a glob of everything ending in .feature, because if I pulled in the entire features directory it would pull in everything beneath it, including all the profile files, etc, which isn't what I wanted since each profile file sets the default capybara driver to a different value. Once you specify -r
as an option to cucumber, all autoloading of any file is halted.
default: --format pretty
chrome: --format pretty -r features/support/profiles/chrome.rb -r features/all_features -r features/step_definitions
firefox: --format pretty -r features/support/profiles/firefox.rb -r features/all_features -r features/step_definitions
celerity: --format pretty -r features/support/profiles/celerity.rb -r features/all_features -r features/step_definitions
firefox.rb (the 'profile' file):
require File.dirname(__FILE__) + "/../env.rb"
Capybara.configure do |config|
config.default_driver = :selenium_firefox
end
selenium_firefox.rb (where I register the driver, and set some tag capability which I've wound up not needing now, as the @selenium_firefox
tag was part of my original attempt at this posted in the question):
# Register a specific selenium driver for firefox
Capybara.register_driver :selenium_firefox do |app|
Capybara::Driver::Selenium.new(app, :browser => :firefox)
end
# Allows the use of a tag @selenium_firefox before a scenario to run it in selenium with firefox
Before('@selenium_firefox') do
Capybara.current_driver = :selenium_firefox
end
feature_runner.thor:
require 'benchmark'
class FeatureRunner < Thor
APP_ROOT = File.expand_path(File.dirname(__FILE__) + "/../")
# One place to keep all the common feature runner options, since every runner in here uses them.
# Modify here, and all runners below will reflect the changes, as they all call this proc.
feature_runner_options = lambda {
method_option :verbose, :type => :boolean, :default => true, :aliases => "-v"
method_option :tags, :type => :string
method_option :formatter, :type => :string
method_option :other_cucumber_args, :type => :string
}
desc "all_drivers_runner", "Run features in all available browsers"
method_option :benchmark, :type => :boolean, :default => false
method_option :threaded, :type => :boolean, :default => true
feature_runner_options.call # Set up common feature runner options defined above
def all_drivers_runner
if options[:threaded]
feature_run = lambda {
thread_pool = []
t = Thread.new do |n|
invoke :firefox_runner
end
thread_pool << t
t = Thread.new do |n|
invoke :chrome_runner
end
thread_pool << t
t = Thread.new do |n|
invoke :celerity_runner
end
thread_pool << t
thread_pool.each {|th| th.join}
}
else
feature_run = lambda {
invoke "feature_runner:firefox_runner", options
invoke "feature_runner:chrome_runner", options
invoke "feature_runner:celerity_runner", options
}
end
if options[:benchmark]
puts "Benchmarking feature run"
measure = Benchmark.measure { feature_run.call }
puts "Benchmark Results (in seconds):"
puts "CPU Time: #{measure.utime}"
puts "System CPU TIME: #{measure.stime}"
puts "Elasped Real Time: #{measure.real}"
else
feature_run.call
end
end
desc "firefox_runner", "Run features on firefox"
feature_runner_options.call # Set up common feature runner options defined above
def firefox_runner
command = build_cucumber_command("firefox", options)
run_command(command, options[:verbose])
end
desc "chrome_runner", "Run features on chrome"
feature_runner_options.call # Set up common feature runner options defined above
def chrome_runner
command = build_cucumber_command("chrome", options)
run_command(command, options[:verbose])
end
desc "celerity_runner", "Run features on celerity"
feature_runner_options.call # Set up common feature runner options defined above
def celerity_runner
command = build_cucumber_command("celerity", options)
run_command(command, options[:verbose])
end
private
def build_cucumber_command(profile, options)
command = "cd #{APP_ROOT} && ./bin/cucumber -p #{profile}"
command += " --tags=#{options[:tags]}" if options[:tags]
command += " --formatter=#{options[:formatter]}" if options[:formatter]
command += " #{options[:other_cucumber_args]}" if options[:other_cucumber_args]
command
end
def run_command(command, verbose)
puts "Running: #{command}" if verbose
output = `#{command}`
puts output if verbose
end
end
Where everything wound up, in relation to the root directory:
.
|____cucumber.yml
|____features
| |____all_features.rb
| |____google_search.feature
| |____step_definitions
| | |____google_steps.rb
| | |____web_steps.rb
| |____support
| | |____custom_formatters
| | | |____blah.rb
| | |____env.rb
| | |____paths.rb
| | |____profiles
| | | |____celerity.rb
| | | |____chrome.rb
| | | |____firefox.rb
| | |____selenium_drivers
| | | |____selenium_chrome.rb
| | | |____selenium_firefox.rb
| | | |____selenium_ie.rb
| | | |____selenium_remote.rb
| | |____selenium_drivers.rb
|____tasks
| |____feature_runner.thor
| |____server_task.rb
Output of thor -T
feature_runner
--------------
thor feature_runner:all_drivers_runner # Run features in all available browsers
thor feature_runner:celerity_runner # Run features on celerity
thor feature_runner:chrome_runner # Run features on chrome
thor feature_runner:firefox_runner # Run features on firefox
Now I can run something like:thor feature_runner:all_drivers_runner --benchmark
This would run all features on all capybara drivers in a thread for each driver, benchmnarking the results.
Orthor feature_runner:celerity_runner
This would run all features only on celerity.
But I can now also supply some other options to the thor command which get passed onto cucumber such as:--tags=@all_browsers
--formatter=hotpants
--other_cucumber_args="--dry-run --guess --etc"
What a feature file can now look like:
Feature: Start up browser
@all_browsers
Scenario: Search Google
Given I am on the home page
When I fill in the search bar with "Capybara"
And I press "Search"
Then I should see "Capybara"
Seems like a lot of setup, but now if I tag a feature with @all_browsers, I can build out a suite to test against all capybara drivers, in a multi-threaded environment, with one thor command:thor feature_runner:all_drivers_runner --threaded --tags=@all_browsers
Or build out a smoke test suite that runs in celerity:thor feature_runner:celerity_runner --tags=@smoke_test
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With