Project Structure:
├── config
│ └── config.yml
├── features
│ ├── search.feature
│ ├── step_definitions
│ │ └── home_steps.rb
│ └── support
│ ├── env.rb
│ ├── hooks.rb
│ └── page_objects
│ ├── base_page.rb
│ └── home_page.rb
Configuration
config/config.yml
default: &default
browser: chrome
base_url: "http://example.com"
timeout: 30
development:
<<: *default
test:
<<: *default
production:
<<: *default
base_url: "http://production.example.com"
- Stores environment-specific configurations.
- Uses YAML anchors (
&default
) and aliases (<<: *default
) for inheritance.
Environment Setup
features/support/env.rb
require 'capybara/cucumber'
require 'selenium-webdriver'
require 'yaml'
# Load configuration based on the ENV variable or default to 'development'
env = ENV['ENV'] || 'development'
config = YAML.load_file('config/config.yml')[env]
# Register Selenium driver with dynamic browser options
Capybara.register_driver :selenium do |app|
browser = (config['browser'] || 'chrome').to_sym
options = case browser
when :chrome
Selenium::WebDriver::Chrome::Options.new
when :firefox
Selenium::WebDriver::Firefox::Options.new
else
raise "Unsupported browser: #{browser}"
end
Capybara::Selenium::Driver.new(app, browser: browser, options: options)
end
# Configure Capybara
Capybara.configure do |capybara_config|
capybara_config.default_driver = :selenium
capybara_config.app_host = config['base_url']
capybara_config.default_max_wait_time = config['timeout']
end
- Reads configurations from the YAML file.
- Dynamically sets up the Selenium driver based on the configuration.
- Configures Capybara settings.
Hooks
features/support/hooks.rb
Before do
# Code to execute before each scenario
puts "Starting scenario: #{scenario.name}"
end
After do |scenario|
if scenario.failed?
# Take a screenshot or perform cleanup
save_screenshot("screenshots/#{scenario.name}.png")
end
puts "Finished scenario: #{scenario.name}"
end
- Utilizes Cucumber hooks to execute code before and after scenarios.
- Includes dynamic actions like taking screenshots on failure.
Page Object Model with Dynamic Programming
features/support/page_objects/base_page.rb
class BasePage
include Capybara::DSL
def initialize
wait_until_page_loaded
end
def wait_until_page_loaded
# Can be overridden by subclasses
end
def self.element(name, locator)
define_method(name) do
find(locator)
end
end
def self.elements(name, locator)
define_method(name) do
all(locator)
end
end
def method_missing(method_name, *args, &block)
if respond_to_missing?(method_name)
send(method_name, *args, &block)
else
super
end
end
def respond_to_missing?(method_name, include_private = false)
self.class.instance_methods.include?(method_name) || super
end
end
- Base class for all page objects.
- Uses dynamic programming to define element accessor methods.
element
and elements
class methods dynamically create instance methods.
features/support/page_objects/home_page.rb
class HomePage < BasePage
element :search_field, '#search'
element :submit_button, '#submit'
def wait_until_page_loaded
has_selector?('#main-content')
end
def search_for(query)
search_field.set(query)
submit_button.click
end
end
- Represents the Home Page using POM.
- Inherits from
BasePage
. - Defines page-specific elements and actions.
Step Definitions
features/step_definitions/home_steps.rb
Given('I am on the home page') do
visit '/'
@home_page = HomePage.new
end
When('I search for {string}') do |query|
@home_page.search_for(query)
end
Then('I should see results for {string}') do |query|
expect(page).to have_content(query)
end
- Uses page objects within step definitions.
- Keeps steps high-level and readable.
Feature File
features/search.feature
Feature: Search Functionality
Scenario: Search for a term
Given I am on the home page
When I search for "Capybara"
Then I should see results for "Capybara"
- Defines a BDD-style feature.
- Utilizes Given-When-Then syntax.
Explanation
- Capybara & Selenium: Used for browser automation and interaction.
- Cucumber: Provides the BDD framework.
- YAML Config: Externalizes configuration, making it easy to switch environments.
- Driver Setup: Dynamically configures the browser driver based on the YAML file.
- Hooks: Executes code before and after each scenario, allowing setup and teardown processes.
- POM: Organizes code by pages, improving maintainability.
- Dynamic Programming: Ruby's metaprogramming defines methods dynamically, reducing boilerplate code.
Running the Tests
Execute the tests using:
ENV=development cucumber
- Set the
ENV
variable to switch configurations. - The tests will run using the specified environment settings.