How to separate page objects from code logic in Selenium- java - java

I am learning and trying to separate locators from actual code in selenium. I have already separated them but I need guidance on more optimization, how can I optimize the code more? Is the Page Object design model used to store only locators? Or can we store their methods too. Can someone please explain with reference to below code?
Link: https://www.goibibo.com/
Actual code with Logic(TC_01Test.java) and Base.java class initializes driver
public class TC_01Test extends Base {
WebDriver driver;
#BeforeTest
public void initialize() throws IOException {
driver = initializeDriver();
}
// Sign In functionality
#Test
public void SignIn() throws InterruptedException {
TC_01 tc02 = new TC_01(driver);
tc02.siginLink().click();
System.out.println(driver.getWindowHandle());
driver.switchTo().frame("authiframe");
System.out.println(driver.getWindowHandle());
tc02.mobileNumber().sendKeys(prop.getProperty("phoneNumber"));
System.out.println("number entered");
tc02.submitButton().click();
System.out.println("button clicked");
driver.switchTo().defaultContent();
System.out.println(driver.getWindowHandle());
tc02.closePopup().click();
}
// SignUp functionality
#Test
public void SignOut() {
TC_01 tc01 = new TC_01(driver);
tc01.sigupLink().click();
driver.switchTo().frame("authiframe");
tc01.mobileNumber().sendKeys(prop.getProperty("phoneNumber"));
tc01.submitButton().click();
driver.switchTo().defaultContent();
tc01.closePopup().click();
}
#AfterTest
public void closeBrowser() {
driver = tearDown();
}
}
Below is the code for Page Object(TC_01.java) created for above test case.
public class TC_01 {
WebDriver driver;
public TC_01(WebDriver driver) {
this.driver = driver;
}
// driver.findElement(By.xpath("//a[#id='get_sign_in']"))
// mobileNumber= driver.findElement(By.xpath("//input[#id='authMobile']")
// driver.findElement(By.id("mobileSubmitBtn"))
// driver.findElement(By.xpath("//div[#class='popContent']/a"))
By signinLink = By.xpath("//a[#id='get_sign_in']");
By signupLink = By.xpath("//a[#id='get_sign_up']");
By mobileNumber = By.xpath("//input[#id='authMobile']");
By submitButton = By.id("mobileSubmitBtn");
By closePopup = By.xpath("//div[#class='popContent']/a");
public WebElement siginLink() {
return driver.findElement(signinLink);
}
public WebElement sigupLink() {
return driver.findElement(signupLink);
}
public WebElement mobileNumber() {
return driver.findElement(mobileNumber);
}
public WebElement submitButton() {
return driver.findElement(submitButton);
}
public WebElement closePopup() {
return driver.findElement(closePopup);
}
}

Answering on your question - yes, you can store methods in PO classes as well. Furthermore, it's a good practice.
Regarding your code optimization - it's better to express business behavior instead of granular technical actions. Also, instead of returning WebElement methods and then perform actions (click, sendKeys etc) in the Test class you can simply perform such actions in PO class.
Check the code below.
public void enterFirstName() {
driver.findElement(firstName).sendKeys("abc");
}
public void enterLastName() {
driver.findElement(lastName).sendKeys("qwerty");
}
public void pressSubmitButton() {
driver.findElement(submitButton).click();
}
// instead of invocation all of these methods above in test class you can simply do this:
public void loginWithValidCredentials(String firstNameValue, String lastNameValue) {
driver.findElement(firstName).sendKeys(firstNameValue);
driver.findElement(lastName).sendKeys(lastNameValue);
driver.findElement(submitButton).click();
}
// Thus your test will look like:
#Test
public void loginTest() {
POclass po = new POclass();
po.loginWithValidCredentials("yourName", "yourNameABC");
// some assert() methods...
}
This is much simplier.
BTW, it's useful to know and use PageFactory concept - https://www.guru99.com/page-object-model-pom-page-factory-in-selenium-ultimate-guide.html
P.S. - read about "Chain of responsibilities" pattern, but in case you are strong in Java, because this is a quite advanced topic.

Related

Parallel run of Selenium tests (uses ThreadLocal) results in orphaned browsers being opened

I use ThreadLocal for thread safety and run the tests in parallel using Maven failsafe and JUnit. I am running two tests from two feature files to test parallel running.
But I always have the first browser blank. Then the subsequent ones are fine and the tests pass. If I run sequentially, there isn’t any issue.
HookStep class:
public class HookStep {
#Before()
public void beginTest() {
WebDriverFactory.setDriver(Props.getValue("browser.name"));
}
#After()
public void stopTest(Scenario scenario) {
switch (environment) {
case "local":
case "aws": {
if (scenario.isFailed()) {
Screenshots.Shot shot = new Screenshots(Screenshots.CONTEXT_TEST_FAIL)
.takeShot(scenario.getName() + formCounter.getAndIncrement() + "");
scenario.embed(shot.getContent(), "image/png", "Error - ");
}
WebDriverFactory.closeBrowser();
}
}
}
WebDriverFactory class:
public class WebDriverFactory {
private static ThreadLocal<WebDriver> driver = new ThreadLocal<>();
public static synchronized void setDriver(String browser) {
switch (browser) {
case "chrome":
driver = ThreadLocal.withInitial(() -> {
WebDriverManager.chromedriver().setup();
return new ChromeDriver(BrowserOptions.getChromeOptions());
});
prepareBrowser();
break;
case "fireFox":
driver = ThreadLocal.withInitial(() -> {
WebDriverManager.firefoxdriver().setup();
return new FirefoxDriver(BrowserOptions.getFirefoxOptions());
});
break;
default:
throw new IllegalStateException("Unexpected value: " + browser);
}
}
private static void prepareBrowser() {
getDriver().manage().window().maximize();
getDriver().manage().deleteAllCookies();
getDriver().manage().timeouts().pageLoadTimeout(15, TimeUnit.SECONDS);
getDriver().manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);
}
public static synchronized WebDriver getDriver() {
return driver.get();
}
public static void closeBrowser() {
getDriver().quit();
}
}
The StepDef class:
public class SampleStepDef {
private final WorldHelper helper;
public SampleStepDef(WorldHelper helper) {
this.helper = helper;
}
#Given("I click on the URL")
public void iClickOnTheURL() {
helper.getSamplePage().navigateToSite();
}
}
public class WorldHelper {
WebDriverFactory webDriverFactory = new WebDriverFactory();
protected WebDriver webDriver = webDriverFactory.getDriver();
private BasePage basePage;
private SamplePage samplePage;
public SamplePage getSamplePage() {
if(samplePage != null)
return samplePage;
samplePage = PageFactory.initElements(webDriver, SamplePage.class);
return samplePage;
}
}
public class SamplePage extends BasePage {
public SamplePage(WebDriver webDriver) {
super(webDriver);
}
public void navigateToSite() {
webDriver.get("https://www.bbc.co.uk");
webDriver.findElement(By.xpath("//a[contains(text(),\'News\')]")).click();
}
}
public class BasePage extends WorldHelper {
public BasePage(WebDriver driver) {
this.webDriver = driver;
}
}
How can I fix this problem?
I noticed multiple problems associated with your code.
You are making use of ThreadLocal.withInitial(). Ideally speaking this should have been defined when you are instantiating the driver thread local static variable.
So instead of
private static final ThreadLocal<WebDriver> driver = new ThreadLocal<>();
it should have been
private static final ThreadLocal<WebDriver> driver = ThreadLocal.withInitial(() -> {
return null; //Your supplier goes here.
});
There's a clear mess up in your inheritance hierarchy (there's a very good chance that you were trying to create a simple example and perhaps have omitted out the details behind the layers of inheritance), but it wasn't clear as to why does all your page object classes extend WorldHelper
You are having multiple statements at the class level such as this. The problem with these field level initialisations is that they get invoked when the object is constructed. So if the object is being constructed in a different thread, then you run into the problem of the WebDriver initialisation being triggered for that thread. End result: You have a lot of ghost browser instances that keep getting opened up, but no selenium actions are directed to them.
private final WebDriver driver = WebDriverFactory.getDriver();
When working with ThreadLocal variants of WebDriver management, you need to make sure that your calls are always from within your step definitions and never from the constructor or from class level field initialisations such as above.
Here are the list of fixes that you need to do.
Remove all occurrences of private final WebDriver driver = WebDriverFactory.getDriver(); in your code. They are not needed.
Refactor your WebDriverFactory class to look like below (For brevity I have removed off all the commented out code)
public class WebDriverFactory {
private static final ThreadLocal<WebDriver> driver = new ThreadLocal<>();
public static void setDriver(String browser) {
RemoteWebDriver rwd;
switch (browser) {
case "chrome":
WebDriverManager.chromedriver().setup();
rwd = new ChromeDriver(BrowserOptions.getChromeOptions());
break;
case "fireFox":
WebDriverManager.firefoxdriver().setup();
rwd = new FirefoxDriver(BrowserOptions.getFirefoxOptions());
break;
default:
throw new IllegalStateException("Unexpected value: " + browser);
}
driver.set(Objects.requireNonNull(rwd));
prepareBrowser();
}
private static void prepareBrowser(){
getDriver().manage().window().maximize();
getDriver().manage().deleteAllCookies();
getDriver().manage().timeouts().pageLoadTimeout(15, TimeUnit.SECONDS);
getDriver().manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);
}
public static WebDriver getDriver(){
return Objects.requireNonNull(driver.get());
}
public static void closeBrowser() {
getDriver().manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
getDriver().close();
getDriver().quit();
}
}
Since all your page classes seem to be extending from WorldHelper, add a getter method such as below in it (or) ensure that no where in any of your page classes you have a WebDriver field. Whenever you need to get hold of the WebDriver instance, you should do it directly via WebDriverFactory.getDriver() (or) via the getter method such as below in your WorldHelper or whatever base class you are creating.
protected WebDriver getDriver() {
return WebDriverFactory.getDriver();
}
Once you have fixed the afore-mentioned problems, you should be good and shouldn't see any blank browser windows open up.
Note: Please clean up your project on GitHub. I noticed some cloud service provider credentials in it (it could be real credentials or could be fake. I wouldn't know.)
I haven't used webDriverFactory, but I'd try calling driver.set() in the factory class, as in this tutorial:
http://makeseleniumeasy.com/2020/05/27/threadlocal-static-webdriver-for-parallel-execution/

How to use Functions/Methods that require 'driver' parameter in Page Factory Model

In my framework i have baseTest class and BasePageObject class
Every Page extends BasePO
Thing is : a lot of my methods on Pages need to use driver instance. I have method getDriver() and i use it 'n' times on every page
This result in Appium calling the getSession for 120 times per 5 min session
Because i use explicit wait(see code):
[debug] [MJSONWP (ae82d29b)] Calling AppiumDriver.getSession() with args: ["ae82d29b-c0af-46f9-bb13-d6ecc8ff5a00"]
2019-10-10 03:22:45:681 - [debug] [XCUITest] Executing command 'getSession'
How to solve this problem?
I`m not sure what to try without changing whole framework
public class BasePO {
WaitUtils waitUtils;
AssertionUtils asrt;
public BasePO() {
asrt = new AssertionUtils();
waitUtils = new WaitUtils();
loadProperties();
initElements();
}
private void initElements() {
PageFactory.initElements(new AppiumFieldDecorator(getDriver()), this);
}
private void loadProperties() {
}
protected IOSDriver<IOSElement> getDriver() {
return IOSDriverManager.getThreadLocalDriver();
}
}
Driver Manager
public class IOSDriverManager {
public static ThreadLocal<IOSDriver<IOSElement>> webDriver = new ThreadLocal<IOSDriver<IOSElement>>();
public static DesiredCapabilities getIOSCaps() {
Here Are my Caps
}
public static IOSDriver<IOSElement> getThreadLocalDriver() {
IOSDriver<IOSElement> driver = webDriver.get();
if (driver == null) {
createThreadLocalWebDriver();
driver = webDriver.get();
}
return driver;
}
public static void createThreadLocalWebDriver() {
IOSDriver<IOSElement> driver = null;
try {
driver = new IOSDriver<IOSElement>(new URL("http://" + getProperty("accessUser") + ":" + getProperty("accessKey") + "#hub-cloud.browserstack.com/wd/hub"), getIOSCaps());
} catch (Exception e) {
try {
driver = new IOSDriver<IOSElement>(new URL("http://" + getProperty("accessUser") + ":" + getProperty("accessKey") + "#hub-cloud.browserstack.com/wd/hub"), getIOSCaps());
} catch (MalformedURLException e1) {
System.out.println("IOS Driver is not created..!, Please check capabilitis or make sure Appium Server is running.");
}
return;
}
webDriver.set(driver);
}
Any Page
public class dashboardPage extends BasePO {
#iOSXCUITFindBy( id = "dashboardScreen")
private MobileElement dashboardScreen;
public boolean isDashboardScreen(){
waitUtils.waitForElementToBeVisible(dashboardScreen, getDriver());
boolean flag =dashboardScreen.isDisplayed();
return flag;
}
May Be it possible to have less getSession calls without shrinking amount of explicit waits ?
I solved the problem in my own framework by adding a protected WebDriver instance and a protected constructor in BasePageObject which assigns the WebDriver instance. Then, your other PageObjects inherit from BasePageObject, and you can implement another constructor for the PageObject that takes WebDriver as an argument.
When you initialize PageObjects, you can pass your test case's WebDriver instance into the PageObject, so you won't have to call getDriver() all the time. You can use this WebDriver instance to perform actions in your PageObjects, and then your test cases can call PageObject methods without any getDriver().
Here's what my framework looks like in C#, starting with BasePageObject:
public class BasePageObject
{
// protected WebDriver instance
protected IOSDriver<IOSElement> Driver { get; }
protected BasePageObject(IOSDriver<IOSElement> driver)
{
Driver = driver;
}
Then, a regular PageObject denoted SomePageObject:
// inherit from BasePageObject
public class SomePageObject : BasePageObject
{
// implement protected constructor from BasePageObject
public SomePageObject(IOSDriver<IOSElement> driver) : base(driver)
{
}
// we can use the PageObject's driver instance as such
public void DoSomethingWithDriver()
{
// this is protected WebDriver instance from BasePageObject above
Driver.FindElement(SomeLocator);
}
}
Now, tying it all together, here's how it looks in a test case:
public class MyTestClass
{
// initialize your driver somehow
IOSDriver<IOSElement> driver = getThreadLocalDriver();
// declare your PageObject, passing in your Driver instance
SomePageObject myPageObject = new SomePageObject(driver);
// now you can call PageObject methods as such
myPageObject.DoSomethingWithDriver();
}
This is all very generic, and will need to be modified to suit your needs, but this is the design pattern that I have followed in all of my test frameworks with good results.
Try to use page factory initialization methods like this. For your every UI page use separate class and then use page factory in the constructor..
public class MainPage {
IOSDriver<IOSElement> driver;
//constructor to initialize MainPage Class with IOSDriver with pageFactory design pattern
ProgrammesPage prog_page;
public MainPage(IOSDriver<IOSElement> driver) {
this.driver = driver;
// PageFactory.initElements(new AppiumFieldDecorator(driver), this);
PageFactory.initElements(new AppiumFieldDecorator(driver), this);
}
}

How to call methods during run time using java instrumentation

I'm completely new to this instrumentation concept. I have a custom jar file which has lot of methods. Lets assume for now i have start and stop method. Inorder to collect the start and stop metrics i need to call those methods after every click . Instead of doing that is there a way to instrument this. I want this methods to be called for all clickable elements dynamically before and after during runtime. Any advise on this would be great. Thanks in advance. Please find the sample code.
Custom Methods:
Public void start (){
long start = System.currentTimeMillis();
}
public void stop{
long finish= System.currentTimeMillis();
long totalTime = finish - start;
}
Sample Code:
start();
driver.findElement(By.name("username")).sendkeys("###");
stop();
start();
driver.findElement(By.name("password")).sendkeys("###");
stop();
start();
driver.findElement(By.name("login")).click();
stop();
Here's an example instrumentation solution using ByteBuddy, although as I mentioned in comments under this question it's probably not a best approach to solve this.
For this simple example the code only covers a case where invocations on WebDriver and WebElement are chained e.g.:
driver.findElement(By.name("login")).click();
driver.findElement(By.name("logout")).click();
Something like below fragment will not work without additional coding:
WebElement element1 = findElement(By.name("login"));
WebElement element2 = findElement(By.name("logout"));
element2.click();
element1.click();
Instrumentation code:
public class ByteBuddyTest {
public static void main(String[] args) throws Exception {
ByteBuddyAgent.install();
new ByteBuddy()
.redefine(RemoteWebDriver.class)
.visit(Advice.to(WebDriverAdvice.class).on(named("findElement").and(takesArguments(1))))
.make()
.load(ByteBuddyTest2.class.getClassLoader(),
ClassReloadingStrategy.fromInstalledAgent());
new ByteBuddy()
.redefine(RemoteWebElement.class)
.visit(Advice.to(WebElementAdvice.class).on(named("click")))
.make()
.load(ByteBuddyTest2.class.getClassLoader(),
ClassReloadingStrategy.fromInstalledAgent());
InternetExplorerDriver driver = new InternetExplorerDriver();
driver.get("<some webpage>");
driver.findElement(By.id("<some_id>")).click();
}
public static class WebDriverAdvice {
#Advice.OnMethodEnter
public static void enter(#Advice.Origin String method) {
System.out.printf("Driver Method Enter: %s\n", method);
Times.start = System.currentTimeMillis();
}
}
public static class WebElementAdvice {
#Advice.OnMethodExit
public static void exit(#Advice.Origin String method, #Advice.This Object target) {
System.out.printf("Element Method Exit: %s\n", method);
System.out.println("Time: " + (System.currentTimeMillis() - Times.start));
}
}
public static class Times {
public static long start = 0L;
}
}
Example using WebDriverEventListener
public class WebDriverEventListenerTest {
public static void main(String[] args) throws Exception {
InternetExplorerDriver driver = new InternetExplorerDriver();
EventFiringWebDriver eventDriver = new EventFiringWebDriver(driver);
eventDriver.register(new EventHandler());
eventDriver.get("<some webpage>");
eventDriver.findElement(By.id("<some id>")).click();
eventDriver.findElement(By.id("<some id>")).click();
}
public static class EventHandler extends AbstractWebDriverEventListener {
#Override public void beforeFindBy(By by, WebElement element, WebDriver driver) {
System.out.printf("Driver Find By: %s\n", by);
Times.start = System.currentTimeMillis();
}
#Override public void afterClickOn(WebElement element, WebDriver driver) {
System.out.printf("Element Method Exit: %s\n", element);
System.out.println("Time: " + (System.currentTimeMillis() - Times.start));
}
}
public static class Times {
public static long start = 0L;
}
}
It appears you are trying to benchmark code. If so, I suggest using a benchmarking framework, such as Google Caliper, which helps instrument code while nominally impacting the actual performance of the code, and it also helps account for JIT compiling, etc., which may alter the execution time of your method as code is executed repeatedly.

How implement method logout from another class? [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 6 years ago.
I read a lot about this problem but i can not find answer for my case.
I have class with Selenium method
public class PrzesylkiPrzygotowane implements Tools{
private WebDriver driver;
private StringBuffer verificationErrors = new StringBuffer();
//Override
public Narzedzia getNarzedzia() {
return new Narzedzia();
}
public void setUp() throws Exception {
StartEN start = new StartEN(GetParams.getUser(),
GetParams.getPassword());
this.driver = start.getDriver();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#Test
public void testPrzesylkiPrzygotowane() throws Exception {
setUp();
driver.findElement(By.cssSelector("a[href*='?action=GetZbior&arg1=220170'")).click();
driver.findElement(By.cssSelector("button.widgetButton")).click();
driver.findElement(By.id("nazwa")).clear();
Thread.sleep(1000);
driver.findElement(By.id("nazwa")).sendKeys("Mar");
driver.findElement(By.xpath("//body[#id='Body']/div[4]/ul/li/strong[3]")).click();
driver.findElement(By.id("submit_button")).click();
Thread.sleep(1000);
//NPE throw here
getNarzedzia().logout();
}
... rest code.
I made interface for this class
public interface Tools {
public Narzedzia getNarzedzia();
}
"Narzedzia" is a class with group of methods which i use like tools for aplication.
public class Narzedzia{
public WebDriver driver;
boolean acceptNextAlert = true;
public void logout() throws InterruptedException{
//Ustawienia driv = new Ustawienia();
driver.findElement(By.linkText("Wyloguj")).click();
Thread.sleep(1000);
assertTrue(closeAlertAndGetItsText(driver).matches("^Czy na pewno chcesz wyjść z Elektronicznego Nadawcy[\\s\\S] Sprawdź czy wszystkie dane zostały przekazane do placówki\\.$"));
driver.close();
}
public String closeAlertAndGetItsText(WebDriver driv) {
try {
Alert alert = driv.switchTo().alert();
String alertText = alert.getText();
if (acceptNextAlert) {
alert.accept();
} else {
alert.dismiss();
}
return alertText;
} finally {
acceptNextAlert = true;
}
}
...rest code
When i run test rest od method in "Narzedzia" works fine but logout throw error:
java.lang.NullPointerException
With Google you should find many hints for implementing a Singleton in Java.
It should be something like this:
public class SingleObject {
//create an object of SingleObject
private static SingleObject instance = new SingleObject();
//make the constructor private so that this class cannot be
//instantiated
private SingleObject(){}
//Get the only object available
public static SingleObject getInstance(){
return instance;
}
}
Source: Design Pattern - Singleton Pattern (my first search result with Google 'singleton pattern java')
Basically the Constructor has to be private and your getter method has to return a field which holds the object. There are some variants around the tutorial sites. Some of them are thread safe. Choose what you need.
I solved problem.
Made constructor with parameter in "Narzedzia" class
public Narzedzia (WebDriver wd){
this.driver = wd;
}
And call constructor like this in PrzesylkiPrzygorowane
public Narzedzia getNarzedzia() {
return new Narzedzia(this.driver);
}
Test is done and logout doesn't throw NPE.

What are the benefits of returning new page object in page object pattern?

For example, if I were to test Google search, what is the benefit of the Page Object model returning new Google Search Page Object?
E.g.
public class SearchPage {
private final WebDriver driver;
public SearchPage(WebDriver driver) {
this.driver = driver;
}
public SearchPage search(String query) {
WebElement e = driver.findElement(By.name("q")).sendKeys(query);
e.submit();
return new SearchPage(driver);
}
}
vs
public class SearchPage {
private final WebDriver driver;
public SearchPage(WebDriver driver) {
this.driver = driver;
}
public void search(String query) {
WebElement e = driver.findElement(By.name("q")).sendKeys(query);
e.submit();
}
}
Thanks for the help!
One thing that comes to my mind is chaining the methods from SearchPage class. When you would have lets say some higher level class that is responsible for running the tests you could use sth like this:
String actualText = searchPage.search("q").openFirstResult().selectItemFromCombo().checkName().getNameText()
etc. etc.
This makes reading your code very easy, looks almost like a sentence and it is understandable for othe people.

Categories

Resources