Selenium : Code not up to the mark comments on my code - selenium

Recently I have started learning selenium and I have applied to the job.
They asked me to write code for CRUD operation for this website
http://computer-database.herokuapp.com/computers
I am pasting the code here.Although it was running fine on my machine and I used framework as well.
Can anyone help me what is not up to the mark because I have been asked to write down code for the second interview and I don't want to repeat my mistake.
Looking forward for your help.
public class Add {
public static WebDriver driver;
public static WebDriver getdriver(){
System.setProperty("webdriver.gecko.driver",
"/Users/sonali/Downloads/geckodriver");
driver = new FirefoxDriver();
return driver;
}
#Test(priority=1) //Create a computer
public static void create(){
driver=getdriver();
driver.get("http://computer-database.herokuapp.com/computers?f=ACE");
driver.manage().window().maximize();
driver.findElement(By.xpath(".//*[#id='add']")).click();
driver.findElement(By.xpath(".//*[#id='name']")).sendKeys("newtest");
driver.findElement(By.xpath(".//*[#id='introduced']")).sendKeys("2017-03-20");
driver.findElement(By.xpath(".//*[#id='discontinued']")).sendKeys("2017-03-29");
Select s= new Select(driver.findElement(By.id("company")));
s.selectByValue("2");
driver.findElement(By.xpath(".//*[#id='main']/form/div/input")).click();
driver.findElement(By.xpath(".//*[#id='main']/div[1]")).isDisplayed();
System.out.println("Creating data is working");
}
#Test(priority=2) //Search for a computer and check its available
public static void read(){
driver.findElement(By.xpath(".//*[#id='searchbox']")).sendKeys("newtest");
driver.findElement(By.xpath(".//*[#id='searchsubmit']")).click();
driver.findElement(By.linkText("newtest")).click();
System.out.println("Reading data is working");
}
#Test(priority=3) // Update a computer name and company
public static void update(){
driver.findElement(By.id("name")).sendKeys("one");
Select s= new Select(driver.findElement(By.id("company")));
s.selectByValue("5");
driver.findElement(By.xpath(".//*[#id='main']/form[1]/div/input")).click();
driver.findElement(By.xpath(".//*[#id='main']/div[1]")).isDisplayed();
System.out.println("Updating computer is working fine");
}
#Test(priority=4) // Deleting computer from the list
public static void delete(){
driver.findElement(By.xpath(".//*[#id='searchbox']")).sendKeys("newtestone");
driver.findElement(By.xpath(".//*[#id='searchsubmit']")).click();
driver.findElement(By.linkText("newtestone")).click();
driver.findElement(By.xpath(".//*[#id='main']/form[2]/input")).click();
driver.findElement(By.xpath(".//*[#id='main']/div[1]")).isDisplayed();
System.out.println("Deleting computer is working fine");
}
}

For the code to be actually useful, it needs to be :
Readable
Maintainable
Structured properly
Try developing a framework for the tests i.e.:
Separate the driver generation to a driver factory class.
Separate the selectors and the respective actions to some other functional or page based classes.
Use assertions to verify. (an exception not appearing does not mean the functionality is working)
e.g.
driver.findElement(By.xpath(".//*[#id='main']/div[1]")).isDisplayed();
It will not matter what it returns as the code does not do anything with it.
It should be -
Assert.assertTrue(driver.findElement(By.xpath(".//*[#id='main']/div[1]")).isDisplayed());
Or better yet-
Assert.assertTrue(updatePage.isupdateDisplayed());
Try putting comments in the code which might make it more easy to understand
Run tests through a runner / xml.

I agree with what #amita said in her answer.
There is a lot you can do to improve on your code but it will take a bit of study, so not sure if you'll be able to follow my advice in time but hopefully this will be helpful to further your understanding of test automation with Selenium WebDriver.
Learn about the Page Object Model design pattern. This is probably the most popular design pattern used with WebDriver. There are lots of free tutorials online but here's a good one to get you started.
Once you have a good idea of Page Object Model you can enhance it by learning about LoadableComponent. Again lots of free tutorials online, here's one I picked at random
You're using XPath extremely heavily. Make sure you understand the preferred hierarchy of locator methods and use them appropriately. ID should be your first preference. XPath and CSS you last resort. For example,
By.xpath(".//*[#id='add']") //Never use this
By.id("add") //When you could have just used the ID locator
Study how TestNG works, in particular Annotations like #BeforeTest,
#AfterTest, and so on. Your tests should be independent, so avoid setting them up in such a way that they require you to force their priority order. It's well documented here but again lots of tutorials online to help you through it.
There's more but if you get your head round all that you'll have a very good base from which to build further. I wish you all the best with your interview

Here is the Answer to your Question:
Considering it as a Interview Question and writing it at a Interview Venue, I think you have done a commendable work.
A few words about the solution:
As you integrated TestNG, apart from #Test Annotation consider using #BeforeTest and #AfterTest Annotation and Assert Class too.
Consider generating some log messages to the console which helps in debugging your own code.
Consider moving the WebDriver instance initialization within #BeforeTest annotation.
Consider starting your #Test with priority=0.
Your xpath like .//*[#id='add'] are not proper, consider using valid xpath e.g. //input[#id='name']
Induce proper ExplicitWait i.e. WebDriverWait while trying to search for elements on new webpages.
When perform some #Test try to validate the Result through Assert Class.
Consider adding the imports wisely.
Once you create a WebDriver instance consider releasing it after your #Test within #AfterTest Annotation.
Here is the minimal code to Create a Computer by the name Debanjan
package demo;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class Q44852473_GOOD_CODE_sonali_arjun
{
public static WebDriver driver;
String myname = "Debanjan";
#BeforeTest
public void initDriver()
{
System.out.println("=====Test Started=====");
System.setProperty("webdriver.gecko.driver", "C:/Utility/BrowserDrivers/geckodriver.exe");
System.out.println("=====Initializing Webdriver=====");
driver = new FirefoxDriver();
}
//Create a Computer
#Test(priority=0)
public void create()
{
driver.get("http://computer-database.herokuapp.com/computers");
driver.manage().window().maximize();
driver.findElement(By.id("add")).click();
WebDriverWait wait1 = new WebDriverWait (driver, 10);
WebElement name = wait1.until(ExpectedConditions.elementToBeClickable(By.xpath("//input[#id='name']")));
name.sendKeys(myname);
driver.findElement(By.xpath("//input[#id='introduced']")).sendKeys("2017-07-01");
driver.findElement(By.xpath("//input[#id='discontinued']")).sendKeys("2017-07-01");
Select select = new Select(driver.findElement(By.id("company")));
select.selectByValue("1");
driver.findElement(By.xpath("//input[#class='btn primary']")).click();
WebDriverWait wait2 = new WebDriverWait (driver, 10);
WebElement searchbox = wait2.until(ExpectedConditions.elementToBeClickable(By.xpath("//input[#id='searchbox']")));
WebElement add_success_ele = driver.findElement(By.xpath("//section[#id='main']/div[#class='alert-message warning']/strong"));
String success = add_success_ele.getText();
Assert.assertTrue(success.contains("Done"));
System.out.println("Computer "+myname+" - created Successfully");
}
#AfterTest
public void tearDown()
{
driver.quit();
System.out.println("=====Test Completed=====");
}
}
Enhancements:
This Solution can be modified by using other TestNG annotations documented here.
This Solution can be enhanced by implementing through POM (Page Object Model) documented here.
This Solution can be further enhanced by implementing POM through PageFactory documented here.
Let me know if this Answers your Question.

Related

How do I use the junit5 platform launcher api to discover tests from a queue?

I'm looking to distribute tests to multiple instances of the junit5 standalone console whereby each instance reads off of a queue. Each instance of the runner would use the same test.jar on the classpath, so I'm not trying to distribute the byte code of the actual tests here, just the names of the tests / filter pattern strings.
From the junit 5 advanced topics doc, I think the appropriate place to extend junit 5 to do this is using the platform launcher api. I cobbled this snippet together largely with the sample code in the guide. I think this is what I need to write but I'm concerned I'm oversimplifying the effort involved here:
// keep pulling test classes off the queue until its empty
while(myTestQueue.isNotEmpty()) {
String classFromQueue = myTestQueue.next(); //returns "org.myorg.foo.fooTests"
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(selectClass(classFromQueue)).build();
SummaryGeneratingListener listener = new SummaryGeneratingListener();
try (LauncherSession session = LauncherFactory.openSession()) {
Launcher launcher = session.getLauncher();
launcher.registerTestExecutionListeners(listener);
TestPlan testPlan = launcher.discover(request);
launcher.execute(testPlan);
}
TestExecutionSummary summary = listener.getSummary();
addSummary(summary);
}
Questions:
Will repeatedly discovering and executing in a while loop violate the normal test lifecycle? I'm a little fuzzy on whether discovery is a one time thing that's supposed to happen before all executions.
If I assume that it's ok to repeatedly discover then execute, I see the HierarchicalTestEngine may be an even better place to read from a queue since that seems to be used for implementing parallel execution. Is this more suitable for my use case? Would the implementation be essentially the same as what I have above except maybe I wouldn't need to handle accumulating test summaries?
Approaches I do not want to take:
I am not looking to use the new features of junit 5 aimed at parallelizing test execution within the same jvm. I'm also not looking to divide the tests or classes up ahead of time; starting each console runnner instance with a pre-determined subset of tests.
Short Answer
The code posted in the question (loosely) illustrates a valid approach. There is no need to create a custom engine. Leveraging the platform launcher api to repeatedly discover and execute tests does work. I think it's worth highlighting that you do not have to extend junit5 This isn't executed through an extension that you need to register as I'd originally assumed. You're just simply leveraging the platform launcher api to discover and execute tests.
Long Answer
Here is some sample code with a simple queue of tests class names that exist on the class path. While the queue is not empty, an instance of the testNode class will discover and execute each of the three test classes and write a LegacyXmlReport.
TestNode Code:
package org.sample.node;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.LauncherSession;
import org.junit.platform.launcher.TestPlan;
import org.junit.platform.launcher.core.LauncherConfig;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
import org.junit.platform.reporting.legacy.xml.LegacyXmlReportGeneratingListener;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.Queue;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
public class TestNode {
public void run() throws FileNotFoundException {
// keep pulling test classes off the queue until its empty
Queue<String> queue = getQueue();
while(!queue.isEmpty()) {
String testClass = queue.poll();
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(selectClass(testClass)).build();
LauncherConfig launcherConfig = LauncherConfig.builder()
.addTestExecutionListeners(new LegacyXmlReportGeneratingListener(Paths.get("target"), new PrintWriter(new FileOutputStream("log.txt"))))
.build();
SummaryGeneratingListener listener = new SummaryGeneratingListener();
try (LauncherSession session = LauncherFactory.openSession(launcherConfig)) {
Launcher launcher = session.getLauncher();
launcher.registerTestExecutionListeners(listener);
TestPlan testPlan = launcher.discover(request);
launcher.execute(testPlan);
}
}
}
private Queue<String> getQueue(){
Queue<String> queue = new LinkedList<>();
queue.add("org.sample.tests.Class1");
queue.add("org.sample.tests.Class2");
queue.add("org.sample.tests.Class3");
return queue;
}
public static void main(String[] args) throws FileNotFoundException {
TestNode node = new TestNode();
node.run();
}
}
Tests executed by TestNode
I'm just showing 1 of the three test classes since they're all the same thing with different class names.
They reside in src/main/java and NOT src/test/java. This is an admittedly weird yet common pattern in maven for packaging tests into a fat jar.
package org.sample.tests;
import org.junit.jupiter.api.Test;
public class Class1 {
#Test
void test1() {
System.out.println("Class1 Test 1");
}
#Test
void test2() {
System.out.println("Class1 Test 2");
}
#Test
void test3() {
System.out.println("Class1 Test 3");
}
}

Fitnesse wiki unable to call selenium method correctly

I am trying to write a simple fixture that opens the browser and navigates to www.google.com. When I run the wiki page, it passes with all green, but the browser never opens up (I don't think the method even gets called by the wiki). Can someone take a look at my fixture and wiki to see what I am doing wrong? Many thanks in advance,
Here is the Wiki -
!|SeleniumFitness|
|URL |navigateToSite?|
|http://www.google.com| |
After Running -
!|SeleniumFitnesse| java.lang.NoSuchMethodError: org.openqa.selenium.remote.service.DriverCommandExecutor.<init>(Lorg/openqa/selenium/remote/service/DriverService;Ljava/util/Map;)V
|URL |The instance decisionTable_4.setURL. does not exist|navigateToSite?
|http://www.google.com|!The instance decisionTable_4.navigateToSite. does not exist |
Here is the Fixture -
package FitNesseConcept.fitNesse;
import java.util.Properties;
import org.junit.BeforeClass;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.BeforeMethod;
//import com.google.common.base.Preconditions.*;
//import com.google.common.collect.Lists;
import fit.ColumnFixture;
public class SeleniumFitnesse extends ColumnFixture {
public static ChromeDriver driver = null;
private String navigateToSite = "";
public String URL = "";
public SeleniumFitnesse() {
Properties props = System.getProperties();
props.setProperty("webdriver.chrome.driver", "/home/ninad/eclipse-workspace/chromedriver");
driver = new ChromeDriver();
}
// SET-GET Methods
public String getURL() {
return URL;
}
public void setURL(String uRL) {
URL = uRL;
}
public String getNavigateToSite() {
return navigateToSite;
}
public void setNavigateToSite(String navigateToSite) {
this.navigateToSite = navigateToSite;
}
// Navigate to URL
public void navigateToSite() throws Throwable {
System.out.println("Navigating to Website");
try {
driver.navigate().to(URL);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
You are getting some good recommendations as comments - but to answer your question directly, for an old-style ColumnFixture, which is what you have written, the method "navigateToSite" is indeed not going to be called.
These styles of fixtures are not often used anymore, Slim is preferred, and your fitnesse instance in its documentation will show you how to use Slim style. However, for a column fixture as you have written, if you want a method to be called it needs to be a "?" following name of the method in the header row.
See basic docs for column fixture:
http://fitnesse.org/FitNesse.UserGuide.FixtureGallery.BasicFitFixtures.ColumnFixture
You are mis-using column fixture, even granted the old style though. Column fixture's pattern is "here is a series of columns that represent inputs, now here is a method call I want to make to get the output and check result". Navigating a website does not often fit that pattern. In old style fitnesse it would probably be approached by an ActionFixture:
http://fitnesse.org/FitNesse.UserGuide.FixtureGallery.BasicFitFixtures.ActionFixture
In the newer Slim style, a good fit for navigation and checking where you are would be a Scenario Table.
http://www.fitnesse.org/FitNesse.UserGuide.WritingAcceptanceTests.SliM.ScenarioTable
In general doing WebDriver / Selenium tests through a wiki is worth extra thought as to whether it's your best medium. Fitnesse is really designed to be a collaborative tool for documenting and verifying business requirements, directly against source code.
Here's an example of how to do with a ColumnFixture, although again ColumnFixture not exactly appropriate:
|url|navigateToUrl?|
|www.google.com| |
java class:
public String url;
public void navigateToUrl() {
}
You could return an "OK" if it navigates alright, or return the title of the page as opposed to void if you wanted.

Hard and soft assertion for Webdriver

I am a newbie in automation testing, and m really confused about assert and verify.Since, I am using TestNG, according to my research I came to know that in webdriver, we dont have verify, we have hard and soft assertion. But when I search for it, I get all mixed answers. and nowhere can I find a detailed example.
For soft assertion I saw someone using 'customverification' but when I try to write that in my program I get error, asking to create a class or interface.
Can someone please help me in this. I am studying through internet so its really hard to get correct answers.
Thanks
package revision;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.Assert;
import org.testng.annotations.Test;
public class Six {
WebDriver driver=new FirefoxDriver();
#Test
public void SandyOne() {
driver.get("file:///C:/Users/Sandeep%20S/Desktop/Test.html");
Assert.assertTrue(IsElementPresent(By.xpath("//input[#id='custom']")), "tab was missing");
driver.findElement(By.xpath("//input[#id='custom']")).sendKeys("abcd");
System.out.println("1st program");
System.out.println("blah 1");
System.out.println("blah 2");
}
public boolean IsElementPresent(By by) {
try {
driver.findElements(by);
return true;
} catch (org.openqa.selenium.NoSuchElementException e) {
return false;
}
}
}
If you have an assert that fails the test will be stopped, where for verify the test will be continued and the error will be logged.
Ideally you will have only one assert per test (e.g. the correct page has loaded) and verify will be used to check the information on that page in this case.
Therefore, if the correct page was not loaded, there is no point in checking the stuff on the page is correct.
You can get an idea and a visual example here.
Your test probably fails here:
Assert.assertTrue(IsElementPresent(By.xpath("//input[#id='custom']")), "tab was missing");
because IsElementPresent returns false. One way to avoid that would be:
try {
Assert.assertTrue(IsElementPresent(By.xpath("//input[#id='custom']")), "tab was missing");
driver.findElement(By.xpath("//input[#id='custom']")).sendKeys("abcd");
}
catch (AssertionError ae) {
//ignore
}
However, catching errors is quite ugly code. A better way would be to use WebDriver.findElements(By by) and check if the resulting list is empty or not.
Hard Assertions:Test execution stops as soon as assertion failure found.
Soft Assertions:Test execution continues even if assertion failure found.
e.g. You have 3 assert statements Assert1,Assert2,Assert3
Now if Assert2 fails in case of hard assertion the test will terminate.
In case of soft assertion it will move to next steps in test and then terminate.
You need to instantiate soft assertion as:
SoftAssertions softAssertion = new SoftAssertions();
softAssertion.assertTrue(condition,message)
In your given code snippet hard assertion makes sense since you cannot move to next step to send text until the input box is found.
In given example you don't need to assert element presence. If it's missing the findElement method will throw an error and you will know that it's not there.
If you have elements's Id, use it instead of xpath. This will make the code more readable and faster:
driver.findElement(By.Id("custom")).sendKeys("abcd");
Instead of directly calling the findElement method, it's recommended to use the PageObject pattern and pick elements with annotations, see PageFactory:
public class TestPage {
#FindBy(id = "custom")
WebElement custom;
private WebDriver driver;
public TestPage (WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
public TestPage inputCustom(String txt) {
custom.sendKeys(txt);
return this;
}
}
#Test
public void SandyOne() {
// ...
TestPage page = new TestPage(driver);
page.inputCustom("abcd");
// ...
}

void org.openqa.selenium.WebElement.click() Note: This element neither has attached source nor attached Javadoc and hence no Javadoc could be found

I wrote the below code :
package LoginMercuryTour;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class LoginMercuryTour {
public static void main(String[] args) throws InterruptedException
{
WebDriver driver=new FirefoxDriver();
String baseUrl="http://newtours.demoaut.com/";
driver.get(baseUrl);
WebElement userName;
WebElement Password;
userName=driver.findElement(By.name("userName"));
Password=driver.findElement(By.name("password"));
userName.sendKeys("tutorial");
Password.sendKeys("tutorial");
driver.findElement(By.name("login")).click();
Thread.sleep(10000);
driver.findElement(By.cssSelector("input[value='oneway']")).click();
Select passengersCnt=new Select(driver.findElement(By.name("passCount")));
passengersCnt.selectByVisibleText("1");
Select Dep=new Select(driver.findElement(By.name("fromPort")));
Dep.selectByVisibleText("London");
}
}
this is ruuning fine without any error. But the click functionality is not working. the button is not getting clicked. While checking the javadoc it is showing
org.openqa.selenium.firefox.FirefoxDriver.FirefoxDriver()
Note: This element neither has attached source nor attached Javadoc and hence no Javadoc could be found.
Note: I am able to click the Login button.
But the second click is not working/
Please Help. I am a beginner to selenium Webdriver.
It's working just fine for me. The Thread.sleep() is not needed so I commented it out. I simplified a few things in your code but other than that, it's all the same. The sleep was 10s so maybe you didn't give it time to finish?
If you aren't going to reuse the username and password elements, there is no need to store them in variables so I removed the declaration and combine the sendKeys() call with the find. I changed the passenger count to 4 to make sure that part was working since the default is 1.
WebDriver driver = new FirefoxDriver();
String baseUrl = "http://newtours.demoaut.com/";
driver.get(baseUrl);
driver.findElement(By.name("userName")).sendKeys("tutorial");
driver.findElement(By.name("password")).sendKeys("tutorial");
driver.findElement(By.name("login")).click();
// Thread.sleep(10000);
driver.findElement(By.cssSelector("input[value='oneway']")).click();
Select passengersCnt = new Select(driver.findElement(By.name("passCount")));
passengersCnt.selectByVisibleText("4");
Select Dep = new Select(driver.findElement(By.name("fromPort")));
Dep.selectByVisibleText("London");
If you want to fix the no Javadoc error, mk08 provided that solution.
As selenium is distributed in form of JARs, you need to add explicitly a java doc location for it. Follow the instruction given in THIS blog (9th & 10th instruction). It should resolve the problem.

In Selenium, What is the command to "wait until the page loading stops"

How to make the WebDriver wait until the page loading stops completely.
Means, it waits and checks whether entire page has been loaded or not, then only it proceeds with the next line execution.
The biggest problem is that there is no generic, one-size-fits-all solution that will work for even a majority of users. The concept of "when is my page finished loading" is rendered nearly meaningless in today's dynamic, AJAX-heavy, JavaScript-dependent web. One can wait for the browser to determine network traffic is complete, but that doesn't take JavaScript execution into account. One could define "complete" as the page's onload event having fired, but that overlooks the possibility of the page using setTimeout(). Furthermore, none of these definitions take frames or iframes into account.
When it comes to Selenium, there are a couple of factors to consider. Remember that the Selenium RC API is 10 years old. When it was designed and developed, the architecture of typical web pages made a method like waitForPageToLoad practical. The WebDriver API, on the other hand, recognizes the current reality. Individual driver implementations usually will try to wait for a page load during an explicit page navigation (e.g., driver.get()), but this wait will be a "best effort", and is not a guarantee. Please note that navigation caused by user interaction (e.g., element.click()) will be less likely to fully wait, because such interactions are asynchronous, and thus inherently have race conditions.
The correct approach for WebDriver is to wait for the element you want to interact with to appear on the subsequent page. This is best accomplished with a WebDriverWait or a similar construct. You might find some of these other constructs in the support library, mainly in those dealing with the Page Object pattern. You could also try setting the implicit wait timeout in your driver instance, but I believe using it obscures intent.
That's actually the default behavior of Selenium - it waits until all requests are complete before going on to the next line of code.
There is a design pattern provided through the Selenium support library SlowLoadableComponent that would do what you want: https://selenium.googlecode.com/svn/trunk/docs/api/java/org/openqa/selenium/support/ui/SlowLoadableComponent.html. The gist is that you write your page object to extend SlowLoadableComponent. You will have to provide implementations for two abstract methods in SlowLoadableComponent: load() and isLoaded()
TheisLoaded() method should check everything you need to consider your page 'loaded'. The load() method performs the actions necessary to load your page object. You specify a load timeout for your page object (I do this through the page object's constructor). When you invoke the get() method on your page object, which is inherited from SlowLoadableComponent, it will call isLoaded(). If your page object is not loaded, it will then call load() to load your page object. It will continue to do this until your page object is loaded or until your timeout expires.
You will have to define yourself what it means for your page object to be loaded, however. There is no out of the box way for Selenium to determine if your particular page object is loaded or not because these determinations are so context-sensitive. For example, consider a page object representing the login page for a web app. It is 'loaded' if the username and password entry text boxes and the submit login button are visible. This does not apply to a page object representing some other page in a web app. You have to custom tailor the 'is loaded' criteria for any given page object.
Here is a simple example. Basic abstract loadable object:
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.SlowLoadableComponent;
import org.openqa.selenium.support.ui.SystemClock;
public abstract class AbstractLoadableComponent<T extends AbstractLoadableComponent<T>> extends SlowLoadableComponent<T> {
public static final int DEFAULT_TIMEOUT_IN_SECONDS = 30;
private final WebDriver driver;
private final int timeoutInSeconds;
public AbstractLoadableComponent(final WebDriver driver, final int timeoutInSeconds) {
super(new SystemClock(), timeoutInSeconds);
this.driver = driver;
this.timeoutInSeconds = timeoutInSeconds;
this.load();
}
public final WebDriver getDriver() {
return driver;
}
public final int getTimeoutInSeconds() {
return timeoutInSeconds;
}
#Override
protected void load() {
PageFactory.initElements(getDriver(), this);
}
}
Basic abstract page object:
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.SlowLoadableComponent;
public abstract class AbstractPage<T extends AbstractPage<T>> extends AbstractLoadableComponent<T> {
private final String url;
public AbstractPage(final WebDriver driver) {
this(driver, driver.getCurrentUrl(), DEFAULT_TIMEOUT_IN_SECONDS);
}
public AbstractPage(final WebDriver driver, final String url) {
this(driver, url, DEFAULT_TIMEOUT_IN_SECONDS);
}
public AbstractPage(final WebDriver driver, final String url, final int timeoutInSeconds) {
super(driver, timeoutInSeconds);
this.url = url;
}
public final String getUrl() {
return url;
}
#Override
protected void load() {
super.load();
if(url != null) {
getDriver().get(url);
}
}
}
Basic concrete page object class for a login page:
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;
import static org.testng.Assert.assertTrue;
public final class LoginPage extends AbstractPage<LoginPage> {
#FindBy(how = How.ID, using = "username")
private WebElement usernameBox;
#FindBy(how = How.ID, using = "password")
private WebElement passwordBox;
#FindBy(how = How.NAME, using = "login")
private WebElement loginButton;
public LoginPage(final WebDriver driver) {
this(driver, driver.getCurrentUrl(), DEFAULT_TIMEOUT_IN_SECONDS);
}
public LoginPage(final WebDriver driver, final String url) {
this(driver, url, DEFAULT_TIMEOUT_IN_SECONDS);
}
public LoginPage(final WebDriver driver, final String url, final int timeoutInSeconds) {
super(driver, url, timeoutInSeconds);
}
#Override
protected final void isLoaded() throws Error {
try {
assertTrue(usernameBox.isDisplayed(), "Username text box is not displayed");
assertTrue(passwordBox.isDisplayed(), "Password text box is not displayed");
assertTrue(loginButton.isDisplayed(), "Login button is not displayed");
} catch(NoSuchElementException nsee) {
throw new Error(nsee);
}
}
}
driver.manage.implicitlywait(3, TimeUnit.Seconds) will hep.