Selenium WebDriver based framework using POM with java - selenium

I am trying to write code to validate a webpage (Test Form with 3 required fields firstname, lastname, phone and 2 buttons submit and clear form) using POM with Selenium WebDriver with Java.
This is the code which I have written so far. I want to confirm whether I am going in the right way.
public class TestForm {
WebDriver driver;
By firstName=By.id("fname");
By lastName=By.id("lname");
By phoneno=By.id("phone");
By submit=By.id("submit");
By clearForm=By.xpath("//tagname[#type='button']");
public TestForm(WebDriver driver)
{
this.driver=driver;
}
public void typeFirstName(String fname)
{
driver.findElement(firstName).sendKeys(fname);
}
public void typeLastName(String lname)
{
driver.findElement(lastName).sendKeys(lname);
}
public void typePhone(String phone)
{
driver.findElement(phoneno).sendKeys(phone);
}
public void clickSubmit()
{
driver.findElement(submit).click();
}
public void clickClearForm()
{
driver.findElement(clearForm).click();
}
}
public class VerifyTestForm {
#Test
public void verifyValidTestForm()
{
WebDriver driver=new FirefoxDriver();
driver.manage().window().maximize();
driver.get("url of the application");
TestForm form=new TestForm(driver);
form.typeFirstName("John");
form.typeLastName("Adams");
form.typePhone("1234567890");
form.clickSubmit();
form.clickClearForm();
driver.quit();
}
}

Most code look good, except following items:
1) By clearForm=By.xpath("//tagname[#type='button']");
tagname should be a correct tag, like button or 'input'
2) After click Submit, the page still stay the form page, If so call clickClearForm should work.
form.clickSubmit();
form.clickClearForm();
3) There is no any check point/validation in your code, all are operateion on page.
// Assume an new page will open after click Submit button
// You need to check the new page opened by check page title if it'll change
// or check an element belongs to the new page is displayed
Assert(driver.getTitle()).toEqual('xxx')
Assert(driver.findElement(xxx).isDisplay()).toBe(true)
// above code may not exact correct, dependent on you choose Junit, TestNG
// or third-party Assert library.
// After click `Clear/Reset` button, you should check all fields reset to default value
Assert(form.readFirstName()).toEqual("")
4) For test class name VerifyTestForm, it's better start or end with Test, like Testxxx or xxxTest

As your code is correct but it is not the way to implementing Page object Model.
You have to use concept of DataProvider to implement framework.
Make a excel sheet and extract the data by using DataProvider.
Make a new class file from where you can read your excel data.
Make a function which return 2d data of the file.
So By using this, The way to implement the framework.
Page object Model generally says that we should have to make the separate page of each module which we are using and return the reference of the last page.

Related

Page Object Patern in Java Selenium - update of elements

I use Page Object Pattern in Java Selenium and I've got problem with updating element after click action.
There is a list of X elements on page and button that enables to scroll them (after clicking on button, new elements are displayed on the list, old ones disappear)
There are two classes:
public abstract class WebPage {
protected WebDriver driver;
WebPage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(new AppiumFieldDecorator(driver, Duration.ofSeconds(30)), this);
}
and
public class ChildClass extends WebPage{
#FindBy(selector)
List<WebElement> elements;
By nextButton = By.id(xxx);
ChildClass(WebDriver driver) {
super(driver);
}
public void iterateAllElements(){
for (WebElement element: elements){
//some action
}
if(buttonIsEnabled())
clickOnButton();
iterateAllElements();
}
}
Please tell me why object "elements" is not initialized with new values after invoking clickOnButton(); function? I cannot get elements that are displayed on 2nd, 3rd page ect
There are still elements from 1st view on the list. It works fine in debug mode, but problem occurs when I want to run it.

Serenity BDD screenplay verify multiple text elements on the webpage

How do I verify multiple text elements and links in the bdd using Serenity BDD ?
I am using below code but using this approach i have to write same copy of code for every element on the webpage which is timeconsuming, is there any alternate way to parametrize and verify values
private static final String APIBUILDER = "app-data-api-card .card-header";
#Subject("the displayed notebook")
public static class APIBUILDER implements Question<String> {
#Override
public String answeredBy(Actor actor) {
return BrowseTheWeb.as(actor).findBy(APIBUILDER).getText();
}
public static Question<String> value() { return new APIBUILDER(); }
You can use the Ensure library
static By FIRST_NAME_FIELD = By.id("first_name");
static By LAST_NAME_FIELD = By.id("last_name");
actor.attemptsTo(
Ensure.that(FIRST_NAME_FIELD).text().isEqualTo("Foo")
Ensure.that(LAST_NAME_FIELD).text().isEqualTo("Bar")
);
If you want soft assertions, you could also do this:
Ensure.enableSoftAssertions();
actor.attemptsTo(
Ensure.that(FIRST_NAME_FIELD).text().isEqualTo("Foo")
Ensure.that(LAST_NAME_FIELD).text().isEqualTo("Bar")
);
Ensure.reportSoftAssertions();
Actually the example with soft assertions doesn't work as intended. When the first check fails then the next one is not executed.

Can "driver.findElement" be used as method and be called in a program

I am using "driver.findElement" multiple times. So can it be saved in a method and called multiple times?
driver.findElement(By.xpath("//tbody[#id='detailsstockid']/tr/td[12]/a/input")).click();
driver.findElement(By.id("supplier_name")).click();
driver.findElement(By.xpath("//select[#id='supplier_name']/option[7]")).click();
driver.findElement(By.id("catagory_name")).click();
driver.findElement(By.id("productname")).sendKeys("AutoProductNew");
driver.findElement(By.id("productcode")).sendKeys("ap02");
You just need a wrapper like this
public static WebElement findElement(By locator){
WebElement anElement = driver.findElement(locator);
return anElement;
}
findElement(By.id("id")).click();
Basically you can call whatever method available to the object
I think what you are trying to do is to simplify your code... maybe make it more human readable. This is a noble goal but I would suggest that you take a different approach. Without more info, I don't know what your typical script is like. From the example code you provided, I would assume that you are going to a few pages, clicking on things, filling in textboxes, etc. I would suggest that you look into the page object model.
There are a LOT of references on the web on this topic, here's a good one to start with.
http://www.seleniumhq.org/docs/06_test_design_considerations.jsp#page-object-design-pattern
The basic concept is create one Class file per page. [Page doesn't always literally mean an entire page. For example, I would create a separate page object for each popup dialog.] What this allows you to do is to consolidate all the code specific to that page into a single location. If you know what an API is and understand it, you will basically create an API for each page/dialog you touch. If done properly, it will greatly simplify and organize your code and will make creating new scripts drastically easier and they will be much easier to read. The benefits are numerous... one big one is code maintenance is made SO much easier. Imagine you have 100 scripts that all start by logging into the site. Let's assume the design of the login page changes... without the page object model, you will have to update all 100 scripts individually. If you are using the page object model, you open the LoginPage.java class, make the change to the Login() method, and you just fixed all 100 scripts.
You may be asking... how does this answer my question? What it does is turns
driver.findElement(By.id("supplier_name")).click();
into
somePage.clickSupplierName();
in your actual script. I think this is more along the lines of what you are asking without answering your specific question. The .findElement code still exists, but is "hidden away" in the page object.
A simple example. I wrote a script, with supporting page objects, to navigate to Amazon.com, search for Star Wars, return the # of found items, and then navigate to the Today's Deals page. The entire code is below.
program.java (Main script)
public class program
{
public static void main(String[] args)
{
WebDriver driver = new FirefoxDriver();
driver.manage().window().maximize();
driver.get("https://www.amazon.com");
// HOME PAGE
HomePage homepage = new HomePage(driver);
homepage.search("star wars");
// SEARCH RESULTS PAGE
SearchResultsPage searchResultsPage = new SearchResultsPage(driver);
System.out.println(searchResultsPage.getNumberOfResults());
// navigate to the Today's Deals page
searchResultsPage.clickTodaysDealsLink();
// TODAY'S DEALS PAGE
// do more stuff
driver.close();
driver.quit();
System.out.println("Done");
}
}
HomePage.java (Home page page object)
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class HomePage
{
WebDriver driver;
private By searchTextboxLocator = By.id("twotabsearchtextbox");
public HomePage(WebDriver driver)
{
this.driver = driver;
}
public void search(String searchString)
{
driver.findElement(searchTextboxLocator).sendKeys(searchString + "\n");
}
}
SearchResultsPage.java (Search results page page object)
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class SearchResultsPage
{
WebDriver driver;
private By todaysDealsLinkLocator = By.linkText("Today's Deals");
private By numberOfResultsLocator = By.id("s-result-count");
public SearchResultsPage(WebDriver driver)
{
this.driver = driver;
}
public String getNumberOfResults()
{
// grabs only the total count of search results from the string in the form "1-16 of 7,488,146 results for "
return driver.findElement(numberOfResultsLocator).getText().split(" ")[2];
}
public void clickTodaysDealsLink()
{
driver.findElement(todaysDealsLinkLocator).click();
}
}

Managing web driver code separately

Can you help me with this question.
I am working on a test automation framework and I want to keep my webdriver code separately and initiate from my test cases.
please help me in this. - Thanks
Go for the POM Model with Encapsulation . In the Example below I have created a Test Setup class which can then be used in all the test classes.
So this would be you TestSetup Class-
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.IE;
namespace dummy
{
[SetUpFixture]
public class TestSetup
{
public static IWebDriver driver;
[OneTimeSetUp]
public void testSetup()
{
if (driver == null)
{
//Local Tests
driver = new FirefoxDriver();
}
}
public static bool IsElementPresent(By by)
{
try
{
driver.FindElement(by);
return true;
}
catch (NoSuchElementException)
{
return false;
}
}
[OneTimeTearDown]
public void TearDown()
{
driver.Quit();
}
}
}
And Below can be you TestCase Class . Notice that you are just calling the test setup .So your test class will inherit all the functions from the Primary class-TestSetup
using System;
using OpenQA.Selenium;
using NUnit.Framework;
using System.Threading;
namespace dummy
{
[TestFixture]
[Parallelizable]
public class TESTCASE
{
IWebDriver driver = TestSetup.driver;
[Test]
public void siteVisit()
{
driver.Navigate().GoToUrl("http://google.com");
Assert.IsTrue(String.Equals("Google, driver.Title));
}
}
}
Hi if i have understood it correctly then please go for POM (Page object Framework)
1.The main advantage of Page Object Model is that if the UI changes for any page, it don’t
require us to change any tests, we just need to change only the code within the page objects
(Only at one place).
2.Page Object model is writing all the functionalities / reusable components of a page that
we want to automate in a separate class.
3.As per google wiki Page object **"Within your web app’s UI there are areas that your tests
interact with. A Page Object simply models these as objects within the test code. This
reduces the amount of duplicated code and means that if the UI changes, the fix need only
be applied in one place."**
Example : google home page
1.Say now if we consider our google home page as Home page.
2.For the above pages we will create class as HomePage.class.
3.In class we will identify and write reusable methods which are specific to Home page.
4.'google home page' which will have many options like Search, Sign In, +You, Images etc.
5.Now all functionalities that we want to automate should have reusable methods/components
for each page.
6.as our main page is google page we can navigate to other pages by clicking on any link
from the google page. When ever we are navigating to other page, we need to return that page
object. Else Return the current page object as this action doesn't navigate to a other page
represented by another Page Object.
advantages.
1.There is clean separation between test code and page specific code such as locators
(or their use if you’re using a UI map) and layout.
2.There is single repository for the services or operations offered by the page rather than
having these services scattered through out the tests.
In both cases this allows any modifications required due to UI changes to all be made in one place.Click for more on POM

Using SpecFlow, Selenium and FluentAutomation causing problems

I am currently using Specflow with Selenium and FluentAutomation, and am running into significant problems with maintaining state between steps in specflow.
See my example code below:
[Binding]
public class RegistrationSteps : FluentTest
{
[Given(#"I create an account")]
public void GivenICreateAnAccount()
{
new HomePage(this)
.Go()
.StartRegistration()
.EnterDetailsAndClickSubmit(); // takes me to deposit page
}
[When(#"Deposit '(.*)' dollars in my account")]
public void GivenDepositMoneyInMyAccount(int amount)
{
new DepositPage(this)
.EnterDetailsAndClickSubmit(amount);
}
}
My problem is:
In the first step the page is loaded using Go() and everything happens fine
In the second step my tests continue, here I expect I am on a different page, based in the Submit in the previous
Because I am no on a different PageObject it gets confused, I don't use Go because the previous step shouldve brought me here, and at this stage it wont find the expected elements
So my question is, how can I use one browser session and several PageObjects across multiple Specflow tests?
According to the FluentAutomation doc, you should do something like this:
[Binding]
public class RegistrationSteps : FluentTest
{
private PageObject _currentPage;
[Given(#"I create an account")]
public void GivenICreateAnAccount()
{
_currentPage = new HomePage(this)
.Go()
.StartRegistration()
.EnterDetailsAndClickSubmit(); // takes me to deposit page
}
[When(#"Deposit '(.*)' dollars in my account")]
public void GivenDepositMoneyInMyAccount(int amount)
{
_currentPage = _currentPage
.EnterDetailsAndClickSubmit(amount);
}
}
Provided that you return the page object that is switched to in the EnterDetailsAndClickSubmit method of your concrete page object like:
Public PageObject EnterDetailsAndClickSubmit() {
// [.. enter details here and click submit ..]
return this.Switch();
}