Industry Grade Selenium Webdrive Template - java

I've recently started learning about selenium WebDriver, and I've learned a lot of stuff from different sources but I don't have a good idea of how should a clean/professional grade script should look like and how should it's content be written.
This is an example of a login that I've created as a test, what could I change?
package Facebook;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
public class Login {
WebDriver driver = new ChromeDriver();
public void login() throws InterruptedException
{
driver.get("http://www.facebook.com");
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
WebElement user = driver.findElement(By.id("email"));
WebElement password = driver.findElement(By.id("pass"));
user.sendKeys("user_test");
password.sendKeys("password_test");
Thread.sleep(3000);
user.clear();
password.clear();
WebElement submit = driver.findElement(By.id("u_0_u"));
if(submit.isDisplayed())
{
System.out.println("\u001B31;1m Succes");
}
else
{
System.out.println("\u001B31;2m Fail");
}
}
public static void main(String[] args) throws InterruptedException {
Login obj = new Login();
obj.login();
}
}

You should spend some time learning about the Page Object Model. If you are going to build more than a few tests, it will be a significant boost to organization, keeping your code clean, and lessening the maintenance burden.
Avoid Thread.sleep() and implicit waits. Instead prefer WebDriverWait.
Don't write your own logging/reporting. Instead use JUnit or TestNG. They are well established and will save you a lot of time with not only logging but handling organization of your tests, executions, reporting, etc.
NOTE: Be careful about questions on SO that sound like asking for a code review. There's a whole other site for that, http://codereview.stackexchange.com.

Related

Can I make the WebDrivers as global variables in Java

I want to create a class where I set all the common actions of the WebDrivers such as: waitExplicit, findElement, click. But if I create a method then I have to create the WebDriver and WebDriverWait over and over on each method of the class, I already tried having a class for the Drivers, but when I call the methods, they just create instances over and over, so multiple windows open, I tried this way, but still cannot get to it:
public class AutomationActions{
static LoadProperties prop = new LoadProperties(); //This class has the System.setProperty for the driver
prop.getSysProp(); //***This is the issue, how can I solve this?****
WebDriver driver = new ChromeDriver(); //this will not work without the one above working
WebDriverWait wait = new WebDriverWait(driver, 30);//this will not work without the one above working
public void waitForPageToLoad() throws Exception {
ExpectedCondition<Boolean> pageLoadCondition = new
ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
return ((JavascriptExecutor)driver).executeScript("return document.readyState").equals("complete");
}
};
// WebDriverWait wait = new WebDriverWait(driver, 30); // I want to avoid having to set this in every method
wait.until(pageLoadCondition); //this is supposed to replace the line of code above
}
I don't really work on Java much any more, I've written our framework in C# but I put together some quick classes in Java to show you how I set things up. I use page object model and I recommend you do too so I've written this example using page object model. I've written a simple test that uses Dave Haeffner's (one of the Selenium contributors) demo site, http://the-internet.herokuapp.com.
The basic concepts are:
There is a class BaseTest that holds things that correspond to tests, e.g. setting up the driver at the start of the test, quitting the driver at the end of the test, etc. All of your tests will inherit from BaseTest
There is a class BasePage that holds things that correspond to generic methods for finding elements, clicking on elements, etc. Each of your tests inherit from BasePage. (This is what I think the main part of your question is asking about).
To follow the page object model, each page (or part of a page) is its own class and holds all locators and actions done on that page. For example, a simple login page would have the locators for username, password, and the login button. It would also hold a method Login() that takes a String username and a String password, enters those in the appropriate fields and clicks the Login button.
The final class of this example is a sample test aptly named SampleTest.
You shouldn't have any FindElements() or related calls in your tests, all those should be in the appropriate page object.
This is using TestNG as the unit test library. Use it or JUnit, your preference but if you use JUnit, you will need to change the asserts and the annotations.
Under 'src', I create a folder for page objects, 'PageObjects', and a folder for tests, 'Tests'. Here's what the files look like on disk.
\src
\PageObjects
BasePage.java
DropdownListPage.java
\Tests
BaseTest.java
SampleTest.java
BasePage.java
package PageObjects;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
public class BasePage
{
private WebDriver driver;
private final int shortWait = 10;
public BasePage(WebDriver _driver)
{
driver = _driver;
}
public void ClickElement(By locator)
{
new WebDriverWait(driver, shortWait).until(ExpectedConditions.elementToBeClickable(locator)).click();
}
public WebElement FindElement(By locator)
{
return new WebDriverWait(driver, shortWait).until(ExpectedConditions.presenceOfElementLocated(locator));
}
// add more methods
}
DropdownListPage.java
package PageObjects;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.Select;
public class DropdownListPage extends BasePage
{
private final By dropdownListLocator = By.id("dropdown");
public DropdownListPage(WebDriver _driver)
{
super(_driver);
}
public String GetSelectedOption()
{
return new Select(FindElement(dropdownListLocator)).getFirstSelectedOption().getText();
}
public void SelectOptionByIndex(int index)
{
new Select(FindElement(dropdownListLocator)).selectByIndex(index);
}
}
BaseTest.java
package Tests;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
public class BaseTest
{
public WebDriver driver;
public WebDriver GetChromeDriver()
{
System.setProperty("webdriver.chrome.driver", "C:\\Path\\To\\Chrome\\Driver\\chromedriver.exe");
return new ChromeDriver();
}
#BeforeTest
public void Setup()
{
driver = GetChromeDriver();
driver.manage().window().maximize();
driver.get("http://the-internet.herokuapp.com/dropdown");
}
#AfterTest
public void Teardown()
{
driver.close();
}
}
SampleTest.java
package Tests;
import org.testng.Assert;
import org.testng.annotations.Test;
import PageObjects.DropdownListPage;
public class SampleTest extends BaseTest
{
#Test
public void SampleTestCase()
{
DropdownListPage dropdownListPage = new DropdownListPage(driver);
dropdownListPage.SelectOptionByIndex(1);
Assert.assertEquals(dropdownListPage.GetSelectedOption(), "Option 1", "Verify first option was selected");
}
}
You will need to create a project that contains Selenium for Java and TestNG. Download them and put them on your build path. Create the folder structure as described above and create each of these classes and copy/paste the contents into them. Now all you need to do is run SampleTest as a TestNG Test and it should go.
The test creates a new Chromedriver instance, navigates to the sample page, selects the first option in the dropdown, asserts that the dropdown text is correct, and then quits the driver.
That should get you started. There's a lot of info crammed into the above wall of text, let me know if you have some questions.

Selenium non-deterministic loop over List<WebElement>

I am trying to do some quick tests to learn selenium for web scraping purposes. I am trying to loop over the menu items of the taco bell website. What I find confusing is that the first element of the List, isn't what is selected by the first or second click. The actual selection is usually the 2nd or 3rd element. It is non-deterministic. What am I doing wrong?
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import java.util.List;
public class Main {
static WebDriver driver;
public static void main(String[] args) throws InterruptedException {
System.setProperty("webdriver.chrome.driver", "/Applications/chromedriver");
driver = new ChromeDriver();
driver.get("https://www.tacobell.com/food");
List<WebElement> listOfMenuCategories = driver.findElements(By.cssSelector(".cls-category-card-item-card"));
for(WebElement webElement : listOfMenuCategories){
scanTacoBellMenuCategory(webElement);
}
System.out.println("1: "+listOfMenuCategories.size());
driver.quit();
}
public static void scanTacoBellMenuCategory(WebElement webElement){
webElement.click();
List<WebElement> listOfSubMenuCategories = driver.findElements(By.cssSelector(".product-item"));
for(WebElement submenuCategory : listOfSubMenuCategories){
scanTacoBellSubMenuCategory(submenuCategory);
}
}
public static void scanTacoBellSubMenuCategory(WebElement webElement){
webElement.click();
}
}
Thanks.
UPDATE-------------------------------
I now realize that my example was unnecessarily complicated and intent was not obvious. Here is a more direct example:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import java.util.List;
public class MainTwo {
static WebDriver driver;
public static void main(String[] args) throws InterruptedException {
System.setProperty("webdriver.chrome.driver", "/Applications/chromedriver");
driver = new ChromeDriver();
driver.get("https://www.tacobell.com/food");
List<WebElement> listOfMenuCategories = driver.findElements(By.cssSelector(".cls-category-card-item-card"));
for(WebElement webElement : listOfMenuCategories){
webElement.click();
break;
}
driver.quit();
}
}
The tacobell menu (https://www.tacobell.com/food) has 16 categories in the following order: New, Favorites, Combos, Specialties, Tacos, Burritos, Quesadillas, Nachos, Value Menu, Sweets, Sides, Drinks, Power Menu, Party, Packs, Vegetarian, Breakfast.
When I loop over these items, I "click" the first one in the forEach loop. I would expect that to be the "New" category. I would also expect the result to be repeatable. However, neither of those statements are true. It usually opens one of the "Favorites", "Combos", or "Specialties" menus. However, it can truly open pretty much anything.
It seems as if the Webdriver is non-blocking in some way. In particular, the webElement.click() event doesn't seem to stop the execution of the forEach loop. It is almost as if the the webElement was in another thread.
Why doesn't the "New" menu get displayed when I run the above code and why isn't this deterministic?

Selenium WebDriver -- XPath

Trying to get the script to select the filter button on the top but can't seem to figure out how to input the XPath. I believe it has something to do with it be in a separate iframe.
package chromebrowser;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
public class JavaClass {
public static void main(String[] args) throws InterruptedException {
System.setProperty("webdriver.chrome.driver", "C:\\Newfolder\\chromedriver.exe");
WebDriver driver = new ChromeDriver();
driver.get("https://mlvtwrew73.consilio.com/Relativity/");
driver.manage().window().maximize();
//Thread.sleep(5000); this can be used as a wait command before moving on to the next function
WebElement objWE;
Thread.sleep(9000);
// objWE = driver.findElement(By.linkText("User Status"));
// objWE.click();
driver.switchTo().defaultContent();
driver.findElement(By.xpath("id(\"ctl00_ctl00_itemList_FilterSwitch\")")).click();
// objWE = driver.findElement(By.id("1"));
// driver.close(); will be used to close the site once all testing completes
}
}
Use ID locator -- it's more appropriate here (and faster than XPath):
WebDriverWait wait= new WebDriverWait(driver, 5);
wait.until(ExpectedConditions.elementToBeClickable(By.id("ct‌l00_ctl00_itemList_F‌​ilterSwitch")));
driver.findElement(By.id("ctl00_ctl00_itemList_FilterSwitch")).click();
Looks like you're passing in an incorrect XPath. Try this one:
driver.findElement(By.xpath("//a[#id='ctl00_ctl00_itemList_FilterSwitch']")).click();
If there is then why you are using xpath.
Use id like this
driver.findElment(By.id("ctl00_ctl00_itemList_FilterSwitch"). click ();
I need more clarity regarding your question.
As I have understand, I think you need a requirement of XPath of the Filters image. If i am right, try this:
//div[#class='actionCellContainer']//a/img[#class='itemListActionImage']

creating user defined function for selenium webdriver

I want to create some user defined functions for my webdriver automation code. I tried it, but resulted in failure.
the following is my code
public class snapdeal {
WebDriver driver= new FirefoxDriver();
#Test
public void test() {
// I want open browser in function 1
driver.get("http://amazon.in");
driver.manage().window().maximize();
// Function 2 for searching
driver.findElement(By.xpath("//li[#id='nav_cat_2'")).click();
driver.findElement(By.id("twotabsearchtextbox")).sendKeys("Shoes");
driver.findElement(By.xpath("//input[#class='nav-submit-input']")).click();
driver.findElement(By.xpath("//h2[#class='a-size-medium s-inline s-access-title a-text-normal' and contains(text(), \"Fbt Men's 8876 Casual Shoes\")]")).click();
}
}
How ca i write two functions inside the class?
You were probably trying to nest methods inside test() . It is not possible.
You can use this code below which calls the respective methods in the test(). It works as expected:
public class snapdeal {
static WebDriver driver= new FirefoxDriver();
#Test
public void test() {
//Method1 for Opening Browser.
openBrowser();
// Method2 for searching
searchElement();
}
public static void openBrowser(){
driver.get("http://amazon.in");
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
}
public static void searchElement(){
driver.findElement(By.xpath("//li[#id='nav_cat_2']")).click();
driver.findElement(By.id("twotabsearchtextbox")).sendKeys("Shoes");
driver.findElement(By.xpath("//input[#class='nav-submit-input']")).click();
driver.findElement(By.xpath("//h2[#class='a-size-medium s-inline s-access-title a-text-normal' and contains(text(), \"Fbt Men's 8876 Casual Shoes\")]")).click();
}
}
I think this is like a Hello World for Selenium for you, you could make use defined methods in Java using Junit with the following annotations which can be found here
But as per norms we usually have a #Before method in Junit or #BeforeTest method in testng for setting up the webdriver and the url of AUT, also in your code a couple of xpaths were wrong which were causing the error, Please find below the correct working code with comments:
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.safari.SafariDriver;
public class snapdeal {
public WebDriver driver;
#Before
public void setUP()
{
// I want open browser in function 1
driver= new SafariDriver();
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
driver.get("http://amazon.in");
driver.manage().window().maximize();
}
#Test
public void test() {
// Function 2 for searching
//driver.findElement(By.xpath("//li[#id='nav_cat_2")).click(); //element not needed
driver.findElement(By.id("twotabsearchtextbox")).sendKeys("Shoes");
driver.findElement(By.xpath("//input[#class='nav-submit-input']")).click();
driver.findElement(By.xpath("//*[#title=\"Fbt Men's 8876 Casual Shoes\"]//h2")).click();
}
}
The above code works as desired.
Creating user defined function have two different scope
1) Create function with piece of code and call that function whenever u needed it (Which is done above)
2) Second one creating a custom function wrt each controls like edit boxes , radiobutton , check boxes - etc , so by creating this functions u can make better feasible of your automation framework

Selenium testcase execution speed issues

I have a issue with selenium where i am able to pass the testcase, but the issue is the execution of the testcase is very quick. Is there any way or attribute through which i can control the speed of the execution. I have been facing this problem big time. Any help will be greatly appreciated.
Below is my script for reference.
package test.selenium;
import java.util.concurrent.TimeoutException;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class test{
WebDriver driver;
public TrafficGroupUpdateTestNG() {
}
/**
* #throws java.lang.Exception
*/
#BeforeTest
public void setUp() throws Exception {
// Use Internet Explorer and set driver;
System.setProperty("webdriver.ie.driver",
"D:\\IBM\\Selenium\\IEDriverServer.exe");
driver = new InternetExplorerDriver();
// And now use this to visit URL
driver.get("URL Of JSP");
}
/**
* #throws java.lang.Exception
*/
#AfterTest
public void tearDown() throws Exception {
driver.close();
}
#Test
public final void test() {
final WebElement formElement = driver.findElement(By.id("search"));
final Select drelement = new Select(drelement.findElement(By.id("my_input")));
drelement.selectByIndex(0);
final WebElement submit = driver.findElement(By.id("Submit"));
submit.click();
}
}
It sounds like elements from AJAX calls are not visible or ready when document.ready status is updated. Working with my webdevs, I had them add a waiting class while they loaded things and remove it when they're done.
As a result, I was able to create this "WaitForPageLoad" method that waits until AJAX is finished. It's in C# but simple enough to translate into Java.
I used
Thread.sleep(2000);
A convinient way for making the page wait.
There are many ways you can control speed of the execution..
Implicity wait
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
by providing this imlicity wait, webdriver waits until 20 seconds before it returns object not found
explicit wait
WebDriverWait wait = new WebDriverWait(driver, timeoutInSeconds);
wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
This will wait explicitly for given element..

Categories