How do you recognize a fake Selenium tester? By his code

recognize trees by their fruits

You can recognize a fake Selenium tester by looking at his code.
Look at

  1. how much code he writes in a day
  2. more importantly, look at how good the code is

 

The following code is written by a fake Selenium tester.

 

I have added some comments to explain what the code does.

 

public void sendKeys(String keyword){ 
  //create a web driver wait with a 4 seconds timeout 
  wait = new WebDriverWait(driver, 4);
  try { 
       //wait until the element is visible 
       wait.until(
            ExpectedConditions.visibilityOfElementLocated
                (elementLocator)); 

       //find the element and type the keyword in it 
       driver.findElement(elementLocator).sendKeys(keyword); 
      
       //display information to console about the typed keyword
       System.out.println("Entered keyword: "+ keyword); 
       
       //wait 1 second 
       waitFor(1000); 
  } 
  //if there is an exception while typing the keyword
  catch(RuntimeException e) { 
    //display info to console about trying again
    System.out.println("\ntry again to type keyword:" + keyword); 

    //wait 2 seconds; see below the wait() method 
    wait(); 

    //find the element again and type the keyword
    driver.findElement(elementLocator).sendKeys(keyword); 

    //find the element one more time and type enter 
    sendKeyEnter(); 
  } 
}

public void sendKeyEnter(){ 
  wait.until(
         ExpectedConditions.visibilityOfElementLocated
              (elementLocator)); 
  driver.findElement(elementLocator).sendKeys(Keys.RETURN);
} 

public void wait(){ 
  try { Thread.sleep(2000); 
  } 
  catch (InterruptedException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
  }
} 

public void waitFor(int time){ 
  try { 
    Thread.sleep(time); 
  } 
  catch (InterruptedException e) { 
    // TODO Auto-generated catch block 
    e.printStackTrace(); 
  }
}

 

Not too bad, you would say.

The code is probably created with the intent of

  • trying to type the text in an element that is displayed slowly
  • if the typing fails because the element is not visible yet, wait a few seconds and try again

 

Lets have a second look:

public void sendKeys(String keyword){
   /* 
      why use such a low timeout?  
      if the page is very slow, 4 seconds will not be sufficient; 
      why is the wait object not declared in the sendKeys() method?  
   */ 
   wait = new WebDriverWait(driver, 4); 
   try { 
       /* 
          the result of the next line is the web element; 
          the web element should be saved in a variable and 
          used in the remaining of the method:

           WebElement element = wait.until(.....); 

          also

          is visibilityOfElementLocated the best expected condition 
          if the intent is to type in the element? 
          how about using elementToBeClickable instead? 
       */ 
       wait.until(
           ExpectedConditions.visibilityOfElementLocated
                              (elementLocator)); 

       /*  
          finding the element again is useless; 
          there is no need to find the element again;  
          it was already found by the previous line; 
          just type into it 
       */ 
       driver.findElement(elementLocator).sendKeys(keyword); 

       /* 
           why display info in the console?  
           first, the console can display limited amount of text; 
           second, how is this info from the console useful 
           if you have 250 test methods? 
       */ 
       System.out.println("Entered keyword: " + keyword); 

       /*  
            why wait for 1 second?  
            why wait at all? 
            why not wait for 2, 3, 4 seconds? 
       */ 
       waitFor(1000); 
   } 
   //if there is an exception 
   catch(RuntimeException e) { 
        //same as above about logging info to console 
        System.out.println("\ntry again to type keyword:" + keyword); 

        /* 
           why wait only for 2 seconds?  
           why not 5?  or 10? 
        */ 
        wait(); 

        /*  find the element again and type the keyword 
        */ 
        driver.findElement(elementLocator).sendKeys(keyword); 

        /* 
            no idea why enter has to be pressed; 
            is the web element part of a form so we have to submit it? 
        */ 
        sendKeyEnter(); 
   } 
}

//on what element is this method typing enter?
public void sendKeyEnter(){ 
   wait.until(
       ExpectedConditions.visibilityOfElementLocated
            (elementLocator)); 
   driver.findElement(elementLocator).sendKeys(Keys.RETURN);
} 

/*  for how long does this method wait? 
    this method is duplicated with the next one; 
*/
public void wait(){ 
    try { 
       Thread.sleep(2000); 
    } 
    catch (InterruptedException e) { 
       // the autogenerated code is not removed
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
    }
} 

public void waitFor(int time){ 
    try { 
       Thread.sleep(time); 
    } 
    catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
    }
} 

 

What you just saw is a case of “re-inventing the wheel”.

The code tries to find an web element that is loaded slowly in the page.

 

Selenium WebDriver has already classes that do this such as WebDriverWait and ExpectedConditions.

The same thing can be done with just 3 lines of code:

 

WebElement element = new WebDriverWait(driver, 30).until (
                           elementToBeClickable(elementLocator)); 
element.sendKeys(keyword);

 

How does this work?

The wait object will retry every 500 ms finding the element.
If the element is found and clickable, it is returned so it can be saved in a variable.
Otherwise, the wait object with try again.

Finding the element will be retried until

  • the element is found and is clickabled  or
  • the 30 seconds timeout is up

 

But lets say that we are concerned about stale exceptions or we want more control over the wait times.

Then, we can use FluentWaits:

FluentWait<WebDriver> wait = new FluentWait<WebDriver>(driver)
                        .withTimeout(30, SECONDS)
                        .pollingEvery(2, SECONDS)
                        .ignoring(NoSuchElementException.class)
                        .ignoring(StaleElementReferenceException.class) ;

WebElement element = wait.until (elementToBeClickable(elementLocator)); 

element.sendKeys(keyword);

 

So, do you want to be sure that a Selenium tester is real or fake?

Ask him to show you some of his code.

 

Advertisements

Use a custom driver class instead of utility classes

How can I create my own driver class in Selenium?
When learning test automation, you will want your test methods to be executed in multiple browsers such as Firefox and Chrome.

A Firefox driver is created with the FirefoxDriver class:

WebDriver driver = new FirefoxDriver();

A Chrome driver is created with the ChromeDriver class:

WebDriver driver = new ChromeDriver();

Both driver objects use the WebDriver type but are instantiated by different classes.

Now, you dont want to change the class from FirefoxDriver to ChromeDriver and back every time you want to run the tests in another browser.

Instead, you would like to create the driver based on the browser name:

WebDriver driver = new BrowserDriver(browserName);

How do we do this?

We create a custom driver class that implements the WebDriver interface and overrides its methods.

public final class BrowserDriver implements WebDriver {  

   private WebDriver driver;
   private final String browserName;
   private final int timeout = 30; 
   private final String chromeDriverPath = "./src/chromedriver.exe";  

   public BrowserDriver(String browserName) { 
      this.browserName = browserName; 
      this.driver = createDriver(browserName); 
   }  

   private WebDriver createDriver(String browserName) { 
      if (browserName.toUpperCase().equals("FIREFOX") 
         return firefoxDriver(); 

      if (browserName.toUpperCase().equals("CHROME") 
         return chromeDriver();  

      throw new RuntimeException ("invalid browser name"); 
   } 

   private WebDriver chromeDriver() { 
      if (!new File(chromeDriverPath).exists()) 
        throw new RuntimeException
                    ("chromedriver.exe does not exist!"); 

      try { 
        System.setProperty("webdriver.chrome.driver", 
                           chromeDriverPath); 
        return new ChromeDriver(); 
      } 

      catch (Exception ex) { 
        throw new RuntimeException
              ("couldnt create chrome driver"); 
      } 
   } 

   private WebDriver firefoxDriver() { 
      try { 
         return new FirefoxDriver(); 
      } 
      catch (Exception ex) {         
         throw new RuntimeException
              ("could not create the firefox driver"); 
     } 
   } 

   @Override 
   public String toString() { 
      return this.browserName; 
   } 

   public WebDriver driver() { 
      return this.driver; 
   }  

   @Override 
   public void close() { 
      driver().close();   
   }  

   @Override 
   public WebElement findElement(By locator) { 
      return driver().findElement(locator); 
   }  

   @Override 
   public List findElements(By arg0) { 
      return driver().findElements(arg0); 
   }  

   @Override 
   public void get(String arg0) { 
      driver().get(arg0); 
   }  

   @Override 
   public String getCurrentUrl() { 
      return driver().getCurrentUrl(); 
   }  

   @Override 

   public String getPageSource() { 
      return driver().getPageSource(); 
   }  

   @Override 
   public String getTitle() { 
      return driver().getTitle(); 
   }  

   @Override 
   public String getWindowHandle() { 
      return driver().getWindowHandle(); 
   }  

   @Override 
   public Set getWindowHandles() { 
     return driver().getWindowHandles(); 
   }  

   @Override 
   public Options manage() { 
      return driver().manage(); 
   }  

   @Override 
   public Navigation navigate() { 
      return driver().navigate(); 
   }  

   @Override 
   public void quit() { 
     driver().quit(); 
   }  

   @Override 
   public TargetLocator switchTo() { 
      return driver().switchTo(); 
   }  

   @Override 
   public  X getScreenshotAs(OutputType target
   throws WebDriverException { 
      return ((TakesScreenshot) driver())
                    .getScreenshotAs(target);
   } 

}

 

The only method that is not defined in the WebDriver interface is createDriver().
createDriver() creates a driver object based on the browser name and stores it in the driver variable.

All other methods just override the WebDriver interface methods.

In the test method, you can now use the new BrowserDriver class instead of FirefoxDriver and ChromeDriver:

WebDriver driver = new BrowserDriver(“CHROME”);

driver.get(“http://www.bestbuy.com”);

WebElement searchBox = driver.findElement(searchBoxLocator);

 

The custom driver class can add as well new methods.

 

Lets say that you want to have find element methods that use the WebDriverWait and ExpectedConditions classes:

public WebElement findVisibleElement(By locator) {
   WebElement element = new WebDriverWait(driver(), timeout)
                .until( visibilityOfElementLocated (locator));
   return element;
}

public WebElement findClickableElement(By locator) {
   WebElement element = new WebDriverWait(driver(), timeout)
               .until( elementToBeClickable (locator));
   return element;
}

public WebElement findHiddenElement(By locator) {
   WebElement element = new WebDriverWait(driver(), timeout)
              .until( presenceOfElementLocated (locator));
   return element;
}

You may be tempted to create a utility class, move these methods there and then use the utility class as a parent for the page classes.

Resist this temptation as it leads to bad things 🙂

Avoid utility classes in your automation projects.

More about utility classes very soon on this blog.

Instead of the utility class, you can move these methods to your custom driver class.

public final class BrowserDriver implements WebDriver {  

   private WebDriver driver; 
   private final String browserName; 
   private final int timeout = 30; 
   private final String chromeDriverPath = "./src/chromedriver.exe"; 

   public BrowserDriver(String browserName) { 
      this.browserName = browserName; 
      this.driver = createDriver(browserName); 
   }  

......................................................................

   @Override 
   public WebElement findElement(By locator) { 
     WebElement element = new WebDriverWait(driver(), timeout) 
                        .until( visibilityOfElementLocated(locator)); 
     return element; 
   }  

   @Override 
   public List findElements(By arg0) { 
     return driver().findElements(arg0); 
   }

   public WebElement findClickableElement(By locator) { 
      WebElement element = new WebDriverWait(driver(), timeout) 
                       .until( elementToBeClickable(locator)); 
      return element; 
   } 

   public WebElement findHiddenElement(By locator) { 
      WebElement element = new WebDriverWait(driver(), timeout) 
                      .until( presenceOfElementLocated(locator)); 
      return element; 
 }

}