Creating your First Proper Test

Search for “Reddit” on DuckDuckGo and Select a Search Result

Creating the Cucumber Scenario

When creating our Cucumber scenarios, we want to keep them simple and write them in a declarative style, sticking simply to the business logic, rather than writing them in an imperative style.  A great article on this can be found at http://itsadeliverything.com/declarative-vs-imperative-gherkin-scenarios-for-cucumber.

  1. Right-click on the src/test/resources/features directory, select ‘New’ –> ‘File’ and create a file called ‘SearchScenarios.feature’
  2. Open up the ‘SearchScenarios.feature’ file and add the following test scenario. We can also add a Cucumber tag either above the ‘Scenario’ (to apply to the specific scenario) or above the ‘Feature’ (to apply to all scenarios within the feature)
    Feature: Search Scenarios
     As a user of DuckDuckGo, I want to be able to search for stuff
     
     @Chrome
     Scenario: 01. Search and select a result
       Given an internet user is on the search page
       When they search for "Reddit homepage"
       And they view the first result
       Then they see the Reddit homepage
    
    

    If you’ve been following all the blog posts in this series, you will notice that we have reused some existing steps in this scenario, such as;
    “Given (?:an internet|a DuckDuckGo) user is on the (?:base|search) page”

    The other undefined step definitions will be highlighted as being undefined still.

Creating the Undefined Step Definitions

  1. Highlight some of the ‘When they search for “<string>”‘ step in IntelliJ until a yellow light bulb icon appears next to it
  2. Click the yellow light bulb icon and select ‘Create step definition’ –> ‘SearchSteps (steps)’
    • An undefined step definition should be automatically added to ‘SearchSteps’ like below…
      • If ‘SearchSteps (steps)’ is not listed, choose one that is listed then cut and paste the step definition in to ‘SearchSteps’ class
      • Also rename the String parameter to something more suitable (e.g. ‘searchTerm’)
        package steps;
        
        import cucumber.api.PendingException;
        import io.cucumber.java.en.When;
        import pages.Page;
        import pages.BasePage;
        
        public class SearchSteps extends Page {
        
            @When("^they search for \"([^\"]*)\"$")
            public void theySearchFor(String searchTerm) throws Throwable {
                // Write code here that turns the phrase above into concrete actions
                throw new PendingException();
            }
        }
  3. Highlight some of the ‘And they view the first result’ step in IntelliJ until a yellow light bulb icon appears next to it
  4. Click the yellow light bulb icon and select ‘Create step definition’ –> ‘RedditSteps (steps)’
    • An undefined step definition should be automatically added to ‘RedditSteps’ like below…
      • If ‘RedditSteps (steps)’ is not listed, choose one that is listed then cut and paste the step definition in to ‘RedditSteps’ class
        @And("^they view the first result$")
        public void theyViewTheFirstResult() throws Throwable {
            // Write code here that turns the phrase above into concrete actions
            throw new PendingException();
        }
  5. Highlight some of the ‘Then they see the Reddit homepage’ step in IntelliJ until a yellow light bulb icon appears next to it
  6. Click the yellow light bulb icon and select ‘Create step definition’ –> ‘ValidationSteps (steps)’
    • An undefined step definition should be automatically added to ‘ValidationSteps’ like below…
      • If ‘ValidationSteps (steps)’ is not listed, choose one that is listed then cut and paste the step definition in to the ‘ValidationSteps’ class
        @Then("^they see the Reddit homepage$")
        public void theySeeTheRedditHomepage() throws Throwable {
            // Write code here that turns the phrase above into concrete actions
            throw new PendingException();
        }

Creating the Test Methods

We will now create test methods in Java for each of our steps to use.

Because we are taken to a new ‘search results’ page when we submit a search on Google, we should create a new page class for the search results page.

  1. Right-click on the ‘pages’ package, select ‘New’ –> ‘Java Class’ and create a class file called ‘SearchResultsPage’
  2. Open up the ‘SearchResultsPage’ class and edit it so it looks like below (we want it to inherit from BasePage, as that is where we navigate to the page from when we conduct our search on the ‘BasePage’ [or search page])…
    package pages;
    
    public class SearchResultsPage extends BasePage {
        
    }
  3. Open up the ‘BasePage’ class in the ‘pages’ package and add the following WebElement at the top of the class…
    @FindBy(id = "search_form_input_homepage")
    public WebElement searchField;

    We will use this element in our test method for sending keys to the ‘searchField’. If you right-click on the search field in Chrome web browser and select ‘Inspect Element’, you can see that it has the value of ‘q’ given for its name, this is what we use to find the element in our test framework.

    Also make sure that IntelliJ has added the correct imports…

    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.support.FindBy;
    import org.openqa.selenium.support.How;
    import org.openqa.selenium.Keys;
  4. Add the following test method within the ‘BasePage’ class…
    public SearchResultsPage searchFor(String searchTerm) {
        searchField.clear();
        searchField.sendKeys(searchTerm + Keys.RETURN);
        return instanceOf(SearchResultsPage.class);
    }

    The above method clears the ‘searchField’ of any text, and then sends the passed string to the ‘searchField’, presses the Return key to submit the search, and then returns an instance of the ‘SearchResultsPage’ class after submission.
    Let’s now add a method to assert that the search results have been returned.

  5. Open up the ‘SearchResultsPage’ class in the ‘pages’ package and add the following WebElement at the top of the class…
    @FindBy(how = How.ID, using = "links")
    public WebElement searchResultsContainer;

    We will use this element in our test method for checking that the search results content area is displayed, which is an element specific to the SearchResultsPage (e.g. it does not appear on the BasePage). If you right-click on the search field in Chrome web browser and select ‘Inspect Element’, you can see that it has the value of ‘search’ given for its ID, this is what we use to find the element in our test framework.

    Also make sure that IntelliJ has added the correct imports…

    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.support.FindBy;
    import org.openqa.selenium.support.How;
  6.  Add the following method within the ‘SearchResultsPage’ class…
    public void assertSearchResultsDisplayed() {
        searchResultsContainer.isDisplayed();
    }

    Because we are taken to a new ‘Reddit’ page after selecting the appropriate search result, we should also create another new page class for the ‘Reddit’ page. Because when we click on a search result, we are taken to another page, this would count as a ‘ReturnClick’ method, due to a new page object being returned.

  7. Right-click on the ‘pages’ package, select ‘New’ –> ‘Java Class’ and create a class file called ‘RedditPage’
  8. Open up the ‘RedditPage’ class and edit it so it looks like below (we want it to inherit from ‘SearchResultsPage’ class, as that is where we navigate to the page from when we click through on our search result)…
    package pages;
    
    public class RedditPage extends SearchResultsPage {
    
    }
  9. Open up the ‘SearchResultsPage’ class in the ‘pages’ package and add the following WebElement at the top of the class…
    @FindBy(how = How.CLASS_NAME, using = "result__a")
    public List<WebElement> searchResultLinks;

    If we inspect the element of the first listed result, we can see that every search result link returned by DuckDuckGo falls within an <a> anchor tag with a class name of “result__a”.  In our @FindBy annotation, we can choose to find a List of all WebElements that meet our criteria of having a className of “result__a”.  Also make sure that has IntelliJ added the correct import of

    import java.util.List

    We will use this element in our test method for clicking the link in the searchResult at Index 0 within this list of search results.

  10. Add the following test method within the ‘SearchResultsPage’ class…
    public RedditPage selectFirstListedSearchResult() {
        searchResultLinks.get(0).click();
        return instanceOf(RedditPage.class);
    }
  11. Because we will want to use both the ‘selectFirstListedSearchResult()’ and ‘assertSearchResultsDisplayed()’ methods in our ‘And I view the first result’ step, we can make both these methods private to the class and then make another public method that calls them, like below…
    package pages;
    
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.support.FindBy;
    import org.openqa.selenium.support.How;
    import org.testng.Assert;
    
    import java.util.List;
    
    public class SearchResultsPage extends BasePage {
    
        @FindBy(how = How.CLASS_NAME, using = "result__a")
        public List<WebElement> searchResultLinks;
    
        @FindBy(how = How.ID, using = "links")
        public WebElement searchResultsContainer;
        
        public void viewFirstResult() {
            assertSearchResultsDisplayed();
            selectFirstListedSearchResult();
        }
    
        private RedditPage selectFirstListedSearchResult() {
            searchResultLinks.get(0).click();
            return instanceOf(RedditPage.class);
        }
    
        private void assertSearchResultsDisplayed() {
            Assert.assertTrue(searchResultsContainer.isDisplayed());
        }
    }
  12. Next, we can assert that we see the Reddit homepage by making a method which calls all of the base validation methods we made in the previous section.  To help with timeouts, we will also need to add a WebElement for the redditContentArea and assert that is displayed before asserting the rest. Open up the ‘RedditPage’ class again and add the following…
    package pages;
    
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.support.FindBy;
    import org.testng.Assert
    
    import static utils.extensions.WebElementExtensions.*;
    
    public class RedditPage extends SearchResultsPage {
    
        @FindBy(id = "SHORTCUT_FOCUSABLE_DIV")
        private WebElement redditContentArea;
    
        public void assertIAmOnRedditPage() {
            Assert.assertTrue(redditContentArea.isDisplayed());
            validatePageTitle("reddit:");
            validatePageUrl("https://www.reddit.com");
            validatePageSource("Reddit");
        }
    }

Connecting the Step Definitions to the Test methods

We will now call the test methods from their appropriate step definitions.

  1. Open up the ‘SearchSteps’ class again, and refactor the ‘When they search for “<string>”‘ step so it initialises the page (in this case ‘BasePage’) with the correct web elements rendered and then calls the searchFor() method, like below…
    @When("^they search for \"([^\"]*)\"$")
    public void theySearchFor(String searchTerm) throws Throwable {
        instanceOf(SearchResultsPage.class).searchFor(searchTerm);
    }
  2. Open up the ‘RedditSteps’ class again, and refactor the ‘And they view the first result’ step so it initialises the page (in this case ‘SearchResultsPage’) with the correct web elements and then calls the viewFirstResult() method, like below…
    package steps;
    
    import io.cucumber.java.en.And;
    import pages.SearchResultsPage;
    import pages.Page;
    
    public class RedditSteps extends Page {
    
        @And("^they view the first result$")
        public void theyViewTheFirstResult() {
            instanceOf(SearchResultsPage.class).viewFirstResult();
        }
    }
  3. Open up the ‘RedditSteps’ class again, and refactor the ‘Then they see the Reddit homepage’ step so it initialises the Reddit page with the correct web elements and then class the assertIAmOnRedditPage() method, like below…
    @Then("^they see the Reddit homepage$")
    public void theySeeTheRedditHomepage() {
        instanceOf(RedditPage.class).assertIAmOnRedditPage();
    }

Running the Test Scenario

  1. Right-click on the scenario in the .feature file and click run, it should pass 🙂

In the next section, we will add extension/helper methods for actions we will perform a lot, e.g. clicking, sending text input…etc.

Digiprove sealCopyright secured by Digiprove © 2018
Liked it? Take a second to support Thomas on Patreon!

Previous Article

Next Article

One Reply to “Part 5. Creating your First Proper Test”

  1. I’m having issues with the last few steps.

    It appears to have an issue when trying to piece togheter search steps with the searchresults page

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.