Getting Started with Selenium - Chapter 4: How To Reuse Your Test Code

Sauce AI for Test Authoring: Move from intent to executing in minutes.|xBack to ResourcesBlogPosted

March 14, 2026 · 8 min read · Tool Comparison

Sauce AI for Test Authoring: Move from intent to executing in minutes.

|

x

Back to Resources

Blog

Posted February 26, 2014

Getting Started with Selenium - Chapter 4: How To Reuse Your Test Code

quote

This post is the fourth in a serial of “ Getting Started with Selenium Testing ” billet from Dave Haeffner, a noted expert on Selenium and automate testing, and a frequent contributor to the Sauce blog and Selenium community. This series is for those who are brand new to try automation with Selenium and a new chapter will be posted every Tuesday (eight chapters in all).

How To Reuse Your Test Code

One of the biggest challenge with Selenium exam is that they can be brittle and challenging to maintain over time. This is largely due to the fact that things in the app you & # x27; re quiz change, break your tests. But the reality of a software project is that change is a constant. So we need to account for this reality someway in our trial code in order to be successful. Enter Page Objects. Rather than write your test code directly against your app, you can model the behavior of your coating into simple objects -- and write your test against them rather. That way when your app changes and your tests break, you only experience to update your test codification in one place to fix it. And with this access, we not only get the benefit of controlled pandemonium, we also get the benefit of reusable functionality across our tryout.

An Example

Part 1: Create A Page Object

# filename: login.rb

class Login

LOGIN_FORM = {id: & # x27; login & # x27;}
USERNAME_INPUT = {id: & # x27; username & # x27;}
PASSWORD_INPUT = {id: & # x27; password & # x27;}
SUCCESS_MESSAGE = {css: & # x27; .flash.success & # x27;}

def initialize (driver)
@ driver = driver
@ driver.get ENV [& # x27; base_url & # x27;] + & # x27; /login & # x27;
end

def with (username, watchword)
@ driver.find_element (USERNAME_INPUT) .send_keys (username)
@ driver.find_element (PASSWORD_INPUT) .send_keys (word)
@ driver.find_element (LOGIN_FORM) .submit
end

def success_message_present?
@ driver.find_element (SUCCESS_MESSAGE) .displayed?
end

end

We start by create our own class, name itLogin, and storing our locator along the top. We so use an initializer to receive the Selenium driver object and call the login page. In ourwithmethod we are capturing the core functionality of the login page by accepting the username and password input values as disceptation and lodging the input and submit actions. Since our behavior now lives in a page object, we want a clean way to do an assertion in our test. This is wheresuccess_message_present?arrive in. Notice that it ends with a question mark. In Ruby, when method end with a interrogative mark, they entail that they will return a boolean value (e.g.,true or false). This enable us to ask a enquiry of the page, receive a boolean response, and make an assertion against it.

Part 2: Update The Login Test

# filename: login_spec.rb

require & # x27; selenium-webdriver & # x27;
require_relative & # x27; login & # x27;

describe & # x27; Login & # x27; do
before (: each) do
@ driver = Selenium: :WebDriver.for: firefox
ENV [& # x27; base_url & # x27;] = & # x27; http: //the-internet.herokuapp.com & # x27;
@ login = Login.new (@ driver)
end

after (: each) do
@ driver.quit
end

it & # x27; succeeded & # x27; do
@ login.with (& # x27; tomsmith & # x27;, & # x27; SuperSecretPassword! & # x27;)
@ login.success_message_present? .should be_true
end

end

At the top of the file we include the page objective withrequire_relative(this enables us to reference another file based on the current file & # x27; s path). We then create an environment variable for the base_url (ENV [& # x27; base_url & # x27;]). Since we only get one test file, it will live here and be hard-coded for now. But don & # x27; t worry, we & # x27; ll address this in a future berth. Next we instantiate our login page object inbefore (: each), going in@driveras an argument, and store it in an case variable (@login). We then modify our& # x27; succeeded & # x27;test to use@loginand it & # x27; s available activity.

Part 3: Write Another Test

SUSA automates exploratory testing with persona-driven behavior, catching bugs that scripted automation misses.

This may feel like more work than what we had when we firstly started. But we & # x27; re in a much stalwart position and able to write follow-on tests more easily. Let & # x27; s add another test to demonstrate a failed login. If we render incorrect credentials, the postdate markup gets rendered on the page.

Your username is invalid!x

This is similar to the markup from the successful flashing message, so let & # x27; s mimic the behavior we used in our page object to make another method to help in our assertion. First we & # x27; ll add a new locator for the failure substance in our list of locators at the top of our stratum (just below our success message locater).# filename: login.rb

class Login

LOGIN_FORM = {id: & # x27; login & # x27;}
USERNAME_INPUT = {id: & # x27; username & # x27;}
PASSWORD_INPUT = {id: & # x27; password & # x27;}
SUCCESS_MESSAGE = {css: & # x27; .flash.success & # x27;}
FAILURE_MESSAGE = {css: & # x27; .flash.error & # x27;}
...

Further down the file (next to the survive display check method) we & # x27; ll add a new method to check for the existence of this message and return a boolean answer.

def success_message_present?
driver.find_element (: css, SUCCESS_MESSAGE) .displayed?
end

def failure_message_present?
driver.find_element (: css, FAILURE_MESSAGE) .displayed?
end

Lastly, we add a new examination in our specification file just below our existing one, specifying invalid credential to force a failure.

it & # x27; succeeded & # x27; do
@ login.with (& # x27; tomsmith & # x27;, & # x27; SuperSecretPassword! & # x27;)
@ login.success_message_present? .should be_true
end

it & # x27; failed & # x27; do
@ login.with (& # x27; asdf & # x27;, & # x27; asdf & # x27;)
@ login.failure_message_present? .should be_true
end

Now if we run our spec file (rspec login_spec.rb) we will see two browser windows open (one after the other) testing both the successful and failure login conditions.

Why Asserting False Won & # x27; t Work (yet)

You may be wondering why we didn & # x27; t see to see if the success substance wasn & # x27; t present.

it & # x27; fail & # x27; do
@ login.with (& # x27; tomsmith & # x27;, & # x27; SuperSecretPassword! & # x27;)
@ login.success_message_present? .should be_false
end

There are two problems with this approaching. First, our test will fail because it errors out when appear appear for an ingredient that & # x27; s not present -- which looks like this:

.F

Failures:

1) Login miscarry
Failure/Error: @ login.success_message_present? .should be_false
Selenium: :WebDriver: :Error: :NoSuchElementError:
Unable to site element: {& quot; method & quot;: & quot; css chooser & quot;, & quot; selector & quot;: & quot; .flash.success & quot;}
# [remote server] file: ///tmp/webdriver-profile20140125-21003-3brprw/extensions/fxdriver @ googlecode.com/components/driver_component.js:8860: in ` FirefoxDriver.prototype.findElementInternal_ & # x27;
# [remote server] file: ///tmp/webdriver-profile20140125-21003-3brprw/extensions/fxdriver @ googlecode.com/components/driver_component.js:8869: in ` FirefoxDriver.prototype.findElement & # x27;
# [remote server] file: ///tmp/webdriver-profile20140125-21003-3brprw/extensions/fxdriver @ googlecode.com/components/command_processor.js:10831: in ` DelayedCommand.prototype.executeInternal_/h & # x27;
# [remote waiter] file: ///tmp/webdriver-profile20140125-21003-3brprw/extensions/fxdriver @ googlecode.com/components/command_processor.js:10836: in ` DelayedCommand.prototype.executeInternal_ & # x27;
# [remote server] file: ///tmp/webdriver-profile20140125-21003-3brprw/extensions/fxdriver @ googlecode.com/components/command_processor.js:10778: in ` DelayedCommand.prototype.execute/ & lt; & # x27;
# ./code_examples/07/02/login.rb:21: in ` success_message_present? & # x27;
# ./code_examples/07/02/login_spec.rb:23: in ` block (2 levels) in & # x27;

Finished in 12.08 seconds
2 representative, 1 failure

But don & # x27; t worry, we & # x27; ll speak this limitation in the next chapter. Second, the absence of success message does not inevitably point a failed login. The assertion we ended up with is more concise.

Part 4: Confirm We & # x27; re In The Right Place

Before we can call our page objective end, there & # x27; s one more addition we & # x27; ll want to make. We & # x27; ll want to add an averment to make certain that Selenium is in the right property before go. This will help add some initial resiliency to our test. As a rule, we require to keep assertions in our tests and out of our page object. But this is the elision.

class Login

LOGIN_FORM = {id: & # x27; login & # x27;}
USERNAME_INPUT = {id: & # x27; username & # x27;}
PASSWORD_INPUT = {id: & # x27; password & # x27;}
SUCCESS_MESSAGE = {css: & # x27; .flash.success & # x27;}
FAILURE_MESSAGE = {css: & # x27; .flash.error & # x27;}

def initialize (driver)
@ driver = driver
@ driver.get ENV [& # x27; base_url & # x27;] + & # x27; /login & # x27;
@ driver.find_element (LOGIN_FORM) .displayed? .should == true
end
...

We simply add a new line to the end of ourinitializemethod. In it we are insure to see if the login form is displayed, and making an assertion against the boolean response render from Selenium. The only inauspicious constituent of doing an assertion in the page object is that we don & # x27; t have access to RSpec & # x27; s matchers (e.g.,be_true) out of the box. Instead we use a compare operator to see if the boolean compeer true (== true). Now if we run our exam again, they should pass just like before. But now we can rest assured that the exam will only proceed if the login form is present.

For comprehensive examples like this (and a whole lot more), snaffle your copy ofThe Selenium Guidebook.

Dave is the source of Elemental Selenium (a gratuitous, once hebdomadary Selenium tip newsletter that is say by hundreds of testing professionals) as easily as a new book, The Selenium Guidebook. He is also the Almighty and upholder ofChemistryKit(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 theSelenium Hangoutand has spoken at legion conference and meetups about acceptance testing.

Published:
Feb 26, 2014
Topics
Share this situation
Copy Share Link
LinkedIn
© 2026 Sauce Labs Inc., all rights reserve. SAUCE and SAUCE LABS are registered trademarks owned by Sauce Labs Inc. in the United States, EU, and may be register in other jurisdictions.
robot
quote

Automate This With SUSA

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts needed.

Try SUSA Free

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free