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
Sauce AI for Test Authoring: Move from intent to executing in minutes.
|
x
Blog
Getting Started with Selenium - Chapter 4: How To Reuse Your Test Code
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.
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 FreeTest 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