January 19, 2018

Writing Your First Selenium Test Script

Fundamentally, Selenium works with two pieces of information — the element on a page you want to interact with 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 into a website as an example. With Selenium you would:

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

Selenium is able to find and interact with elements on a page by way of various “locator strategies”. The list includes (sorted alphabetically):

  • Class
  • CSS Selector
  • ID
  • Link Text
  • Name
  • Partial Link Text
  • Tag Name
  • 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 easy 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 markups 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 to write a locator.

How To Find Quality Elements

You want to find an element 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 or class attributes directly on them, look at the element that houses them (a.k.a. the parent element). Often-times the parent element has a unique element that you can use to start with and drill down to the child or descendent element you want to use.

When 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 generally a trivial thing for them to add helpful, semantic markup to make the application more testable. This is especially true when they know the use case you’re trying to automate. The alternative is for you to muddle through it in a lengthy, painful process which will probably yield working test code (but it will be brittle and hard to maintain test code).

Once you’ve identified the target elements for your test, you need to craft a locator using one of Selenium‘s strategies. Let’s step through an example of this as we write our first test.

An Example

Part 1: Find The Elements And Write The Test

Here’s the markup for a standard login form (pulled from the login example on the-internet).


<form name="login" id="login" action="/authenticate" method="post">
   
<div class="row">
    
<div class="large-6 small-12 columns">
      <label for="username">Username</label>
      <input type="text" name="username" id="username">
    </div>

  </div>

  
<div class="row">
    
<div class="large-6 small-12 columns">
      <label for="password">Password</label>
      <input type="password" name="password" id="password">
    </div>

  </div>

    <button class="radius" type="submit"><i class="icon-2x icon-signin"> Login</i></button>
</form>

Note the unique elements on the form. The username input field has a unique id, as does the password input field. The submit button doesn’t, but it’s the only button on the page, so we can easily find it and click it.

  • Coming Soon…
  • Let’s put these elements to use in our first testFirst, we’ll need to create a folder called Tests in our project. Then let’s add a test file to the package called LoginTest.cs. When we’re done our directory structure should look like this.

     

    ├── Tests
    │   └── LoginTest.cs
    └── packages.config
    

    NOTE: Other items in the directory have been omitted for brevity (and will continue to be omitted for the remainder of the course). Things like the Properties directory (and its AssemblyInfo.cs file) as well as the project and solutions files (e.g., SeleniumGuidebookExamples.csprojand SeleniumGuidebookExamples.sln. The name for your project may be different depending on what you named it.

    And here is the code we will add to the test file for our Selenium commands, locators, etc.

    //filename: Tests/LoginTest.cs
    using NUnit.Framework;
    using OpenQA.Selenium;
    using OpenQA.Selenium.Firefox;
    
    namespace Tests
    {
        [TestFixture]
        class LoginTest
        {
            IWebDriver Driver;
    
            [SetUp]
            public void SetUp()
            {
                Driver = new FirefoxDriver();
            }
    
            [TearDown]
            public void TearDown()
            {
                Driver.Quit();
            }
    
            [Test]
            public void ValidAccount()
            {
                Driver.Navigate().GoToUrl("http://the-internet.herokuapp.com/login");
                Driver.FindElement(By.Id("username")).SendKeys("tomsmith");
                Driver.FindElement(By.Id("password")).SendKeys("SuperSecretPassword!");
                Driver.FindElement(By.CssSelector("button")).Click();
            }
        }
    }
    

    After including the requisite classes for NUnit and Selenium we declare a class (e.g., public class LoginTest and add an attribute to it that denotes that this is a test class (e.g., [TestFixtures]). We then declare a field variable to store and reference an instance of Selenium WebDriver (e.g., IWebDriver Driver;).

    Next we add setup and teardown methods with the attributes [SetUp] and [TearDown]. In them we’re creating an instance of Selenium (storing it in Driver) and closing it (e.g., Driver.Quit();). Because of the [SetUp] attribute, the public void SetUp() method will load before our test and the [TearDown] attribute will make the public void TearDown() method load after the test. This abstraction enables us to write our test with a focus on the behavior we want to exercise in the browser, rather than clutter it up with setup and teardown details.

    Our test is a method as well (public void ValidAccount()). NUnit knows this is a test because of the [Test] attribute. In this test we’re visiting the login page by its URL (with Driver.Navigate().GoToUrl()), finding the input fields by their ID (with Driver.FindElement(By.Id())), inputting text into them (with .SendKeys();), and submitting the form by clicking the submit button (e.g., By.CssSelector("button")).Click();).

    If we save this and run it (by clicking TestRunAll Tests or CTRLRA), it will run and pass. But there’s one thing missing — an assertion. In order to find an element to write an assertion against we need to see what the markup of the page is after submitting the login form.

  • Coming Soon…
  • Coming Soon…

Part 2: Figure Out What To Assert

Here is the markup that renders on the page after logging in.

<div class="row">
  
<div id="flash-messages" class="large-12 columns">
    
<div data-alert="" id="flash" class="flash success">
      You logged into a secure area!
      <a href="#" class="close">x</a>
    </div>

  </div>

</div>



<div id="content" class="large-12 columns">
  
<div class="example">
    
<h2><i class="icon-lock"></i> Secure Area</h2>

    
<h4 class="subheader">Welcome to the Secure Area. When you are done click logout below.</h4>

    <a class="button secondary radius" href="/logout"><i class="icon-2x icon-signout"> Logout</i></a>
  </div>

</div>

There are a couple of elements we can use for our assertions in this markup. There’s the flash message class (most appealing), the logout button (appealing), or the copy from the h2 or the flash message (least appealing).

Since the flash message class name is descriptive, denotes a successful login, and is less likely to change than the copy, let’s go with that.

class="flash success"

When we try to access an element like this (e.g., with a multi-worded class) we will need to use a CSS selector or an XPath.

NOTE: Both CSS selectors and XPath work well, but the examples throughout this course will focus on how to use CSS selectors.

A Quick Primer on CSS Selectors

In web design, CSS (Cascading Style Sheets) are used to apply styles to the markup (HTML) on a page. CSS is able to do this by declaring which bits of the markup it wants to alter through the use of selectors. Selenium operates in a similar manner but instead of changing the style of elements, it interacts with them by clicking, getting values, typing, sending keys, etc.

CSS selectors are a pretty straightforward and handy way to write locators, especially for hard to reach elements.

For right now, here’s what you need to know. In CSS, class names start with a dot (.). For classes with multiple words, put a dot in front of each word, and remove the spaces (e.g., .flash.success for class='flash success').

For a good resource on CSS Selectors, I encourage you to check out Sauce Labs’ write up on them.

Part 3: Write The Assertion And Verify It
Now that we have our locator, let’s add an assertion that uses it.

  • Coming Soon…
  • //filename: tests/LoginTest.cs
    // ...
            [Test]
            public void ValidAccount()
            {
                Driver.Navigate().GoToUrl("http://the-internet.herokuapp.com/login");
                Driver.FindElement(By.Id("username")).SendKeys("tomsmith");
                Driver.FindElement(By.Id("password")).SendKeys("SuperSecretPassword!");
                Driver.FindElement(By.CssSelector("button")).Click();
                Assert.That(Driver.FindElement(By.CssSelector(".flash.success")).Displayed);
            }
        }
    }

    With Assert.That we are checking for a true Boolean response. If one is not received the test will fail. With Selenium we are seeing if the success message element is displayed on the page (with .Displayed). This Selenium command returns a Boolean. So if the element is rendered on the page and is visible (e.g., not hidden or covered up by an overlay), true will be returned, and our test will pass.

    When we save this and run it it will run and pass just like before, but now there is an assertion which will catch a failure if something is amiss.

    Just To Make Sure

    Just to make certain that this test is doing what we think it should, let’s change the locator in the assertion to attempt to force a failure and run it again. A simple fudging of the locator will suffice.

    Assert.That(Driver.FindElement(By.CssSelector(".flash.successasdf")).Displayed);

    If it fails then we can feel reasonably confident that the test is doing what we expect and we can change the assertion back to normal before committing our code. This trick will save you more trouble that you know. Practice it often.

  • Coming Soon…
  • Coming Soon…
0 Comments

Leave A Comment

Leave a Reply