I have created a common webdriver class that I can use for my selenium tests. Whenever I create a new test, I get the webdriver from this class.
Here is what I am using to create my driver
package com.atmn.config;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
public class Driver {
private static Log log = LogFactory.getLog(Driver.class);
public static String USERNAME = "name";
public static String ACCESS_KEY = "key";
public static WebDriver driver = createDriver("firefox", "47.0", "Windows 10");
public static WebDriver createDriver(String browser, String version, String os) {
try{
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(CapabilityType.BROWSER_NAME, browser);
capabilities.setCapability(CapabilityType.VERSION, version);
capabilities.setCapability(CapabilityType.PLATFORM, os);
// Launch remote browser and set it as the current thread
return new RemoteWebDriver(
new URL("http://"+USERNAME+":"+ACCESS_KEY+"#ondemand.saucelabs.com:80/wd/hub"),
capabilities);
}
catch(MalformedURLException e)
{
e.printStackTrace();
//log message
log.info(e.getMessage());
}
return null;
}
Here is my test, which will run on SauceLabs use Windows 10 and firefox 47.
package com.atmn.tests;
import com.tplus.atmn.tasks.CommonTasks;
import com.tplus.atmn.objects.TOA6;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openqa.selenium.By;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import com.tplus.atmn.tasks.HttpRequest;
public class Testing extends CommonTasks {
private static Log log = LogFactory.getLog(TB6_TOA.class);
#Test (groups = {"Testing"})
public static void endToEnd() throws Exception {
//Load URL
HttpRequest http = new HttpRequest();
String URL = http.internetWebAddress();
driver.get(URL);
//Page 1
// Item Text
verifyText(Object.ItemText, "Multiple Choice - Single Answer Radio - Vertical");
verifyText(Object.Progress_PercentComplete, "0%");
//URL
verifyNoRedirect();
//Go to next page
driver.findElement(Object.Start).click();
System.out.println("Next Button was clicked.");
}
}
Instead of just running my test on 1 browser in SauceLabs, I want to run this test on multiple browser/OS combinations in parallel. I'm not sure how to change my driver class to make this happen.
So you are using TestNG. That test framework has support for running in parallel built in (it's a setting you can add to a suite file as well as set in other ways). It also allows you to take in arguments to a test method using a DataProvider. There are many, many ways to set up a testing framework and there are a lot of factors that should go into how it's structured.
First off, you are creating a static WebDriver instance in the Driver class which is not what you are going to want. You need multiple instances so what you are really going to want is the factory method on that class that allows you to create WebDrivers for each method as needed. Another consideration you are going to want to take into account is the fact that TestNG does not create new instances of the test class object for each method. Because of that, you either can't store any state as a field on the test class object unless it is done in a way that avoids contention (e.g. a ThreadLocal field for instance). Unfortunately, in the case of a Selenium test, you are going to want to quit the WebDriver when you are finished with it to free up the SauceLabs resources. You could normally do that with an #AfterMethod configuration method, but you aren't guaranteed that the configuration methods will run in the same thread as the test method so using a ThreadLocal field won't work to allow access to the same argument (e.g. the web driver). You could use a map of test methods to web drivers on the class object (TestNG allows you to inject the test method as an argument) but in the case of a data provider to allow for multiple OS / browser combinations it will have the same test method as well. In the end with TestNG and Selenium I find it best (or at least easiest), in my experience, to use DataProviders and just store the fields on the test class object and restrict parallelism to class level.
That being said, here is an example of how to test with parallel methods that. Starting with a simple pom.xml dependency section to show the versions used for this example. Even if your versions are a little different, it should be very similar.
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>2.53.1</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.9.10</version>
</dependency>
</dependencies>
Update the Driver class to make sure it cleanly returns a new driver instance each time. I removed the logger portions of it just to save some space. I also just made it non-instantiable since it is really only a static factory and allowing instances of it doesn't make sense. Also you really probably shouldn't allow access to things like the name and key as they are purely implementation details.
public final class Driver {
private static final String USERNAME = "name";
private static final String ACCESS_KEY = "key";
public static WebDriver createDriver(String browser, String version, String os) {
// Should probably validate the arguments here
try {
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(CapabilityType.BROWSER_NAME, browser);
capabilities.setCapability(CapabilityType.VERSION, version);
capabilities.setCapability(CapabilityType.PLATFORM, os);
return new RemoteWebDriver(new URL("http://" + USERNAME + ":" + ACCESS_KEY + "#ondemand.saucelabs.com:80/wd/hub"),
capabilities);
} catch (MalformedURLException e) {
throw new RuntimeException("Failure forming the URL to create a web driver", e);
}
}
private Driver() {
throw new AssertionError("This is a static class and shouldn't be instantiated.");
}
}
Finally, in the test class itself you need to define the actual test method and a data provider. If you want the same data provider for multiple tests / test classes, that is fine. Refer to the TestNG documentation for details:
http://testng.org/doc/documentation-main.html#parameters-dataproviders
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class FooTest {
#DataProvider(name = "DriverInfoProvider", parallel = true) // data providers force single threaded by default
public Object[][] driverInfoProvider() {
return new Object[][] {
{ "firefox", "47.0", "Windows 10" },
{ "chrome" , "51.0", "Windows 10" },
// etc, etc
};
}
#Test(dataProvider = "DriverInfoProvider")
public void testFoo(String browser, String version, String os) {
WebDriver driver = Driver.createDriver(browser, version, os);
try {
// simple output to see the thread for each test method instance
System.out.println("starting test in thread: " + Thread.currentThread().getName());
// Putting this in a try catch block because you want to be sure to close the driver to free
// up the resources even if the test fails
driver.get("https://www.google.com");
driver.findElement(By.name("q")).sendKeys("Hello, world");
} finally {
driver.quit();
}
}
}
Please note that there are a number of things about these examples that I don't like from an architecture standpoint, but I wanted to give you an idea of a few of the issues to think about and a quick working example that is similar to what you already have. Finally, to run test classes in parallel, you would create and then run a TestNG suite. These are usually defined in XML (though you can also use YAML).
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="MySuiteNameHere" parallel="methods" thread-count="15">
<test name="Selenium Tests">
<classes>
<class name="foo.bar.FooTest"/>
</classes>
</test>
</suite>
Finally running that lead to two tests that loaded google and performed the action along with this sample output:
starting test in thread: PoolService-1
starting test in thread: PoolService-0
Related
Can someone please help me to figure out how to set up saucelabs for testNg on Java?
I’ve tried different guides, and it’s always different variations of set up, here's one of the examples that didn't work for me
#BeforeClass
public void init() throws MalformedURLException {
MutableCapabilities sauceOpts = new MutableCapabilities();
sauceOpts.setCapability("username", "oauth-xxxxxx.yyyyyy-51awdfsa");
sauceOpts.setCapability("accesskey", "xxxxx-32b1-4c4d-ac70-yyyyyyyyy");
DesiredCapabilities options = new DesiredCapabilities();
options.setCapability("sauce:options", "sauceOpts");
options.setCapability("browserVersion", "latest");
options.setCapability("platformName", "windows 10");
options.setCapability("browserName","chrome");
Webdriver driver = new RemoteWebDriver(new URL("https://ondemand.us-west-1.saucelabs.com:443/wd/hub"), options);
}
import com.saucelabs.saucebindings.testng.SauceBaseTest;
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* Example Test for running with the TestNG Sauce Bindings library.
*/
public class SauceBindingsTestngTest extends SauceBaseTest {
#Test
public void correctTitle() {
getDriver().navigate().to("https://www.saucedemo.com");
Assert.assertEquals("Swag Labs", getDriver().getTitle());
}
}
Chrome/Win10 is the default so no setup code is required. If you want to change the configuration, check the Sauce Bindings docs below.
Source Code for example
We have a Demo Java repo with any example you want, just browse the table of contents
We use Sauce Bindings to make the code so simple
This is working for me,
DesiredCapabilities DC = DesiredCapabilities.chrome();
DC.setBrowserName("chrome");
DC.setPlatform(Platform.WIN10);
driver = new RemoteWebDriver(new URL("https://oauth-jayanth_balakrishnan-ae.saucelabs.com:443/wd/hub"), DC); //http://127.0.0.1:4444/wd/hub
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.
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.
I just created a sample project using Serenity + cucumber + java + maven ( > mvn archetype:generate -Dfilter net.serenity-bdd:serenity-cucumber) .
I can see when it brings up a firefox browser and goes to this website:
#DefaultUrl("http://en.wiktionary.org/wiki/Wiktionary")
Question:
From this point, a "WebDriver driver" was created, but how can I obtain/get to that "driver" variable ? I'm trying to get the window ID using driver.getWindowHandle().
Updated: The statements are as followed:
#DefaultUrl("http://en.wiktionary.org/wiki/Wiktionary")
public class DictionaryPage extends PageObject {...}
I looked up the PageObject super class and found this:
public abstract class PageObject extends net.serenitybdd.core.pages.PageObject {
protected PageObject() {
super();
}
protected PageObject(WebDriver driver, Predicate<? super net.serenitybdd.core.pages.PageObject> callback) {
super(driver, callback);
}
public PageObject(WebDriver driver, int ajaxTimeout) {
super(driver, ajaxTimeout);
}
public PageObject(WebDriver driver) {
super(driver);
}
}
So.. the PageObject uses the webdriver variable from another super class. What is the syntax I need to declare to get to this "web driver" variable?
Thanks
Know-nada
=========================
JDelorean!
Thanks a lot for your help. Here are the codes.. Would you please help tp point out what is wrong with the "driver"?
1 - Feature File:
Feature: Amazon user login authentication
Scenario: Amazon user login authentication
Given user is on the "home" page
2 - Step Definitions File:
package com.XXXX.steps;
import net.thucydides.core.annotations.Steps;
import com.XXXX.steps.serenity.User;
import cucumber.api.java.en.Given;
class DefinitionSteps {
#Steps
User user;
#Given("^user is on the \"([^\"]*)\" page$")
public void user_is_on_the_Amazon_page(String pageName)throws Throwable{
user.is_on_the_page(pageName);
}
}
3 - Test Runner class
package com.XXXX;
import cucumber.api.CucumberOptions;
import net.serenitybdd.cucumber.CucumberWithSerenity;
import org.junit.runner.RunWith;
#RunWith(CucumberWithSerenity.class)
#CucumberOptions(features="src/test/resources/features")
public class DefinitionTestSuite {
}
4 - The User Class where I am not able to capture the driver. Please note that at the end of this file, the commented out line is the one that does not work. Also notice that the "driver" was set to the original driver "home.getDriver()" but somehow that value was passed to the "driver" but the driver "home.getDriver()" works just fine.
package com.XXXX.steps.serenity;
import com.XXXX.pages.Home;
import net.thucydides.core.annotations.Step;
import net.thucydides.core.steps.ScenarioSteps;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class User extends ScenarioSteps{
Home home = new Home();
WebDriver driver = home.getDriver();
#Step
public void is_on_the_page(String pageName) throws Throwable {
home.open();
home.getDriver().findElement(By.xpath(home.homeElements.get("Hello. Sign in"))).click();
// driver.findElement(By.xpath(".//*[#id='nav-link-yourAccount']/span[1]")).click();
}
}
PageObject has a getDriver() method, which you should use if you require the use of WebDriver.
I strongly suggest you study the Serenity BDD's Manual if you are going to work with the framework. You should find an answer to most questions there. Alternatively, check Serenity BDD Users Group.
This can be achieved following way.
WebDriver driver = Serenity.getDriver();
driver.findElement(By.xpath....);
For some unknown reason, the Serenity-BDD:Cucumber framework does not allow you to assign its driver to your local variable.
This assignment statement WebDriver driver = getDriver(); will result in a null pointer in the "driver" local variable.
My guess is that the framework needs to protect its getDriver() because it needs to do report on snapshots and scenarios steps.
So you have it. simply use the framework's getDriver() and you can drive from page to page without any problem at all as long as you declare your page class as an extension of the base/Abstract class PageObject.
An example of your page class:
public void HomePage extends PageObject {
}
At the time you instantiate the HomePage class HomePage homePage; the framework's driver is automatically attached to the homePage.
Let say you want to click on an element, you simply state:
getDriver().findElement(By.xpath("locationOfTheTargetElement").click();
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