Posts Tagged ‘Getting Started’

Getting Started With Selenium – Chapter 6: Packaging for Use

March 11th, 2014 by Bill McGee

selenium-logo-160x144This post is the sixth in a series of “Getting Started with Selenium Testing” posts from Dave Haeffner, a noted expert on Selenium and automated testing, and a frequent contributor to the Sauce blog and Selenium community. This series is for those who are brand new to test automation with Selenium and a new chapter will be posted every Tuesday (eight chapters in all).

Packaging For Use

In order to get the most out of our tests and page objects, we’ll need to package them into a more useful structure.

Let’s do that using the examples from the previous write-ups.

Global Setup And Teardown

First we’ll need to pull the test setup and teardown actions out of our tests and into a central place. In RSpec this is straight-forward through the use of a ‘spec_helper’ file.

# filename: spec_helper.rb

require 'selenium-webdriver'

RSpec.configure do |config|

  config.before(:each) do
    @driver = Selenium::WebDriver.for :firefox
  end

  config.after(:each) do
    @driver.quit
  end

end

We need to include the Selenium library here, and by doing so, can remove it from our tests. And by having our test configuration here, we can remove it from the `before(:each)` and `after(:each)` in our tests as well — replacing them with a simple require statement at the top of the file (`require_relative ‘spec_helper’`).

Base URL

Rather than hard-coding a URL in our tests and page objects, We’ll want to put it someplace central, and we’ll want it to be configurable. So let’s create a ‘config.rb’ file in the parent directory and place it there.

# filename: config.rb

ENV['base_url'] ||= 'http://the-internet.herokuapp.com'

By using a conditional when setting the environment variable (`||=`) we are making it so we can override this value when launching our test suite (e.g., `base_url=’http://localhost:4567’`). It essentially means if the environment variable already exists and contains a value, use it. Otherwise, set it to ‘http://the-internet.herokuapp.com’. We can then reference this variable in our page objects where necessary (if we haven’t already).

For instance:

# filename: login.rb

class Login
...

def initialize(driver)
@driver = driver
@driver.get ENV['base_url'] + '/login'
end

...

Folder Organization

It’s about time we create some folders for our specs and page objects. To err on the side of simplicity, let’s call the folders ‘spec’ (for our tests) and ‘pages’ (for our page objects). We are using ‘spec’ since it is a default folder that RSpec will look for.

Here’s everything we should have after creating folders and moving files around:

.
|-- config.rb
|-- Gemfile
|-- pages
| |-- dynamic_loading.rb
| `-- login.rb
`-- spec
|-- dynamic_loading_spec.rb
|-- login_spec.rb
`-- spec_helper.rb

Updating Require Statements

As a result of doing this, we will need to update the require statements in our tests.

# filename: spec/login_spec.rb

require_relative 'spec_helper'
require_relative '../pages/login'

describe 'Login' do
...

# filename: spec/dynamic_loading_spec.rb

require_relative 'spec_helper'
require_relative '../pages/dynamic_loading'

describe 'Dynamic Loading' do
...

Note the use of double-dots (`..`) in the page object require statement. This is how we tell Ruby to traverse up a directory (from our spec directory) before trying to access the page objects folder. The `spec_helper` require remains unchanged since this file lives in the same directory as our tests.

Running Everything

Now that things are cleaned up, we can run everything. To do that we’ll want to make sure to include our new config file. We can do that by specifying it at run time with `rspec –require ./config.rb`, or, `rspec -r ./config.rb` (for short).

Note the `./` before `config.rb`. This tells RSpec that the config file is in the current directory.

Give it a shot. All of the tests should run and pass.

For more examples like this (along with complete working code) — grab your copy of  The Selenium Guidebook.

Previous Chapters: 1. Getting Started | 2. Writing a Good Acceptance Test | 3. Writing Your First Selenium Test | 4. How To Reuse Your Test Code | 5. Writing Resilient Test Code

Dave is the author of Elemental Selenium (a free, once weekly Selenium tip newsletter that is read by hundreds of testing professionals) as well as a new book, The Selenium Guidebook. He is also the creator and maintainer of ChemistryKit (an open-source Selenium framework). He has helped numerous companies successfully implement automated acceptance testing; including The Motley Fool, ManTech International, Sittercity, and Animoto. He is a founder and co-organizer of the Selenium Hangout and has spoken at numerous conferences and meetups about acceptance testing.

Getting Started with Selenium: Chapter 5 – Writing Resilient Test Code

March 4th, 2014 by Bill McGee

selenium-logo-160x144This post is the fifth in a series of “Getting Started with Selenium Testing” posts from Dave Haeffner, a noted expert on Selenium and automated testing, and a frequent contributor to the Sauce blog and Selenium community. This series is for those who are brand new to test automation with Selenium and a new chapter will be posted every Tuesday (eight chapters in all).

Writing Resilient Test Code

Ideally, you should be able to write your tests once and run them across all supported browsers. While this is a rosy proposition, there is some work to make this a reliable success. And sometimes there may be a hack or two involved. But the lengths you must go really depends on the browsers you care about and the functionality you’re dealing with.

By using high quality locators you will be well ahead of the pack, but there are still some persnickity issues to deal with. Most notably – timing. This is especially true when working with dynamic, JavaScript heavy pages (which is more the rule than the exception in a majority of applications you’ll deal with).

But there is a simple approach that makes up the bedrock of reliable and resilient Selenium tests — and that’s how you wait and interact with elements. The best way to accomplish this is through the use of explicit waits.

An Example

Let’s step through an example that demonstrates this against a dynamic page on the-internet. The functionality is pretty simple — there is a button. When you click it a loading bar appears for 5 seconds, then disappears, and gets replaced with the text ‘Hello World!’.

Let’s start by looking at the markup on the page.

Dynamically Loaded Page Elements

Example 1: Element on page that is hidden


At a glance it’s simple enough to tell that there are unique idattributes that we can use to reference the start button and finish text. Let’s add a page object for Dynamic Loading.

Part 1: Create A Page Object

# filename: dynamic_loading.rb

class DynamicLoading

  START_BUTTON  = { css: '#start button' }
  FINISH_TEXT   = { id: 'finish' }

  def initialize(driver)
    @driver = driver
    @driver.get "http://the-internet.herokuapp.com/dynamic_loading/1"
  end

  def start
    @driver.find_element(START_BUTTON).click
  end

  def finish_text_present?
    wait_for { is_displayed? FINISH_TEXT }
  end

  def is_displayed?(locator)
    @driver.find_element(locator).displayed?
  end

  def wait_for(timeout = 15)
    Selenium::WebDriver::Wait.new(:timeout => timeout).until { yield }
  end

end

This approach should look familiar to you if you checked out the last write-up. The thing which is new is the wait_for method. In it we are using a built-in Selenium wait action. This is the mechanism with which we will perform explicit waits.

More On Explicit Waits

It’s important to set a reasonably sized default timeout for the explicit wait. But you want to be careful not to make it too high. Otherwise you run into a lot of the same timing issues you get from implicit waits. But set it too low and your tests will be brittle, forcing you to run down trivial and transient issues.

In our page object when we’re usingwait_for { is_displayed? FINISH_TEXT } we are telling Selenium to to see if the finish text is displayed on the page. It will keep trying until it either returns true or reaches fifteen seconds — whichever comes first.

If the behavior on the page takes longer than we expect (e.g., due to slow inherently slow load times), we can simply adjust this one wait time to fix the test (e.g.,wait_for(30) { is_displayed? FINISH_TEXT }) — rather than increase a blanket wait time (which impacts every test). And since it’s dynamic, it won’t always take the full amount of time to complete.

Part 2: Write A Test To Use The New Page Object

Now that we have our page object we can wire this up in a new test file.

# filename: dynamic_loading_spec.rb

require 'selenium-webdriver'
require_relative 'dynamic_loading'

describe 'Dynamic Loading' do

  before(:each) do
    @driver = Selenium::WebDriver.for :firefox
    @dynamic_loading = DynamicLoading.new(@driver)
  end

  after(:each) do
    @driver.quit
  end

  it 'Waited for Hidden Element' do
    @dynamic_loading.start
    @dynamic_loading.finish_text_present?.should be_true
  end

end

When we run it (rspec dynamic_loading_page.rb from the command-line) it should pass, rather than throwing an exception (like in the last write-up when the element wasn’t present).

As an aside — an alternative approach would be to rescue the exception like this:

  def is_displayed?(locator)
    begin
      @driver.find_element(locator).displayed?
    rescue Selenium::WebDriver::Error::NoSuchElementError
      false
    end
  end

This would enable you to check the negative condition for whether or not an element is displayed. And it can be used with an explicit wait as well (it won’t change it’s behavior).

Part 3: Add A Second Test

Let’s step through one more dynamic page example to see if our explicit wait approach holds up.

Our second example is laid out similarly to the last one, the main difference is that it will render the final result after the progress bar completes. Here’s the markup for it.

Dynamically Loaded Page Elements

Example 2: Element rendered after the fact



In order to find the selector for the finish text element we need to inspect the page after the loading bar sequence finishes. Here’s what it looks like.

Hello World!

Before we add our test, we need to modify our page object to accommodate visiting the different example URLs.

# filename: dynamic_loading.rb

class DynamicLoading

  START_BUTTON  = { css: '#start button' }
  FINISH_TEXT   = { id: 'finish' }

  def initialize(driver)
    @driver = driver
  end

  def visit_example(example_number)
    @driver.get "http://the-internet.herokuapp.com/dynamic_loading/#{example_number}"
  end

  ...

Now that we have that sorted, let’s add a new test to reference the markup shown above (and update our existing test to use the new.visit_example method).

# filename: dynamic_loading_spec.rb


require_relative 'dynamic_loading'

describe 'Dynamic Loading' do

  ...

  it 'Waited for Hidden Element' do
    @dynamic_loading.visit_example 1
    @dynamic_loading.start
    @dynamic_loading.finish_text_present?.should be_true
  end

  it 'Waited for Element To Render' do
    @dynamic_loading.visit_example 2
    @dynamic_loading.start
    @dynamic_loading.finish_text_present?.should be_true
  end

end

If we run these tests (rspec dynamic_loading_spec.rb from the command-line) then the same approach will work for both cases.

Explicit waits are one of the most important concepts in testing with Selenium. Use them often.

For a more in-depth look at explicit waits and how to address cross-browser timing issues — grab your copy of The Selenium Guidebook.

Previous Chapters: 1. Getting Started | 2. Writing a Good Acceptance Test | 3. Writing Your First Selenium Test | 4. How To Reuse Your Test Code

Dave is the author of Elemental Selenium (a free, once weekly Selenium tip newsletter that is read by hundreds of testing professionals) as well as a new book, The Selenium Guidebook. He is also the creator and maintainer of ChemistryKit (an open-source Selenium framework). He has helped numerous companies successfully implement automated acceptance testing; including The Motley Fool, ManTech International, Sittercity, and Animoto. He is a founder and co-organizer of the Selenium Hangout and has spoken at numerous conferences and meetups about acceptance testing.

Getting Started with Selenium – Chapter 3: Writing Your First Selenium Test

February 18th, 2014 by Bill McGee

selenium-logo-160x144This post is the third in a series of “Getting Started with Selenium Testing” posts from Dave Haeffner, a noted expert on Selenium and automated testing, and a frequent contributor to the Sauce blog and Selenium community. This series is for those who are brand new to test automation with Selenium and a new chapter will be posted every Tuesday (eight chapters in all).

Writing Your First Selenium Test

Fundamentally, Selenium works with two pieces of information — the element on a page you want to use and what you want to do with it. This one-two punch will be repeated over and over until you achieve the outcome you want in your application — at which point you will perform an assertion to confirm that the result is what you intended.

Let’s take logging in to a website as an example. With Selenium you would:

1. Visit the main page of a site
2. Find the login button and click it
3. Find the login form’s username field and input text
4. Find the login form’s password field and input text
5. Find the login form and click it

Selenium is able to find and interact with elements on a page by way of various locator strategies. The list includes Class, CSS, ID, Link Text, Name, Partial Link Text, Tag Name, and XPath.

While each serves a purpose, you only need to know a few to start writing effective tests.

How To Find Locators

The simplest way to find locators is to inspect the elements on a page. The best way to do this is from within your web browser. Fortunately, popular browsers come pre-loaded with development tools that make this simple to accomplish.

When viewing the page, right-click on the element you want to interact with and click Inspect Element. This will bring up a small window with all of the HTML for the page but zoomed into your highlighted selection. From here you can see if there are unique or descriptive attributes you can work with.

How To Find Quality Elements

Your focus with picking an effective element should be on finding something that is uniquedescriptive, and unlikely to change. Ripe candidates for this are id and class attributes. Whereas copy (e.g., text, or the text of a link) is less ideal since it is more apt to change. This may not hold true for when you make assertions, but it’s a good goal to strive for.

If the elements you are attempting to work with don’t have unique id and class attributes directly on them, look at the element that houses them (a.k.a. the parent element). Oftentimes the parent element has a unique locator that you can use to start with and drill down into the element you want to use (with CSS selectors).

And if you can’t find any unique elements, have a conversation with your development team letting them know what you are trying to accomplish. It’s not hard for them to add helpful, semantic markup to make test automation easier, especially when they know the use case you are trying to automate. The alternative can be a lengthy, painful process which will probably yield working test code — but it will be brittle and hard to maintain.
(more…)

Getting Started with Selenium – Chapter 2: How To Write a Good Acceptance Test

February 11th, 2014 by Bill McGee

selenium-logo-160x144This post is the second in a series of “Getting Started with Selenium Testing” posts from Dave Haeffner, a noted expert on Selenium and automated testing, and a frequent contributor to the Sauce blog and Selenium community. This series is for those who are brand new to test automation with Selenium and a new chapter will be posted every Tuesday for the next two months.

How To Write a Good Acceptance Test

In order to write Selenium tests that are maintainable, resilient, and performant, there are some simple guidelines to follow:

  • Write atomic and autonomous tests
  • Group like tests together in small batches
  • Be descriptive
  • Use a Test Runner
  • Store tests in a Version Control System

Atomic & Autonomous Tests

Each test needs to be concise (e.g., testing a single feature rather than multiple features) and independent (e.g., sets up its own data rather than relying on a previous test to do it). Doing this may require a mental shift, discipline, and more up front effort. But it will make a dramatic impact on the quality, effectiveness, and maintainability of your tests.

Grouping Tests

As your test suite grows, you should have multiple test files, each containing a small grouping of tests broken out by functionality that they’re exercising. This will go a long way towards organization and maintenance as your test suite grows — as well as faster execution times (depending on your approach to parallelization).

Being Descriptive

Each test file should be named appropriately, and each test within that file should have an informative name (even if it may be a bit verbose). Also, each test (or grouping of tests across test files) should be tagged with some helpful information to provide context and enable more flexible test execution (more on this approach in a future post).

This way all or parts of your test suite can be run, and the results will be informative thanks to helpful naming.

Test Runners

At the heart of every test suite is some kind of a test runner that does a lot of the heavy lifting (e.g., test group execution, easy global configuration for setup and teardown, reporting, etc.). Rather than reinvent the wheel, you can use one of the many that already exists (there’s more than one for every language). And with it you can bolt on third party libraries to extend its functionality if there’s something missing — like parallelization.

Version Control

In order to effectively collaborate with other testers and developers, your test code must live in a version control system of some sort. Look to see what your development team uses and add your code to it. Otherwise, set up one of the following:

Keep in mind that your test code can live in a separate repository from the code of the application you’re testing. Combining them may be advantageous, but if all you’re doing is writing and running tests against web endpoints (which is a majority of what your testing will be with Selenium) then leaving your test code in a separate repository is a fine way to go.

Coming up next, I’ll show you how to write your first Selenium test.

Previous Chapters: Getting Started

Dave is the author of Elemental Selenium (a free, once weekly Selenium tip newsletter that is read by hundreds of testing professionals) as well as a new book, The Selenium Guidebook. He is also the creator and maintainer of ChemistryKit (an open-source Selenium framework). He has helped numerous companies successfully implement automated acceptance testing; including The Motley Fool, ManTech International, Sittercity, and Animoto. He is a founder and co-organizer of the Selenium Hangout and has spoken at numerous conferences and meetups about acceptance testing.

Getting Started with Selenium Testing – 1st in a Series

February 4th, 2014 by Bill McGee

selenium-logo-160x144This post is the first in a series of “Getting Started with Selenium Testing” posts from Dave Haeffner, a noted expert on Selenium and automated testing, and a frequent contributor to the Sauce blog and Selenium community. This series is for those who are brand new to test automation with Selenium and a new chapter will be posted every Tuesday for the next two months.

The First Thing You Need To Know

Selenium is a software robot sent from the future to help us test web applications. But keep in mind that it’s not one of those fancy shape-shifting robots than can run really fast. Instead it’s more like one of those really strong robots that’s not very fast and is best suited for accomplishing a certain objective.

That is to say — Selenium is really good at a specific set of things. If you know what those are and stick to them then you will be able to easily write reliable, scalable, and maintainable tests that you and your team can trust.

But before we go too much further, there are a few things you’ll want to get sorted prior to writing your first test.

Define a Test Strategy

A great way to increase your chances of automated web testing success is to focus your efforts by mapping out a testing strategy. The best way to do that is to answer four questions:

1. How does your business make money (or generate value for the end-user)?
2. How do your users use your application?
3. What browsers are your users using?
4. What things have broken in the application before?

(more…)