Error during converting Selenium Java project to Page Object Design Pattern - java

I created 4 classes also after I decided to convert my project into this design pattern. I moved my codes inside the related classes into the methods. While compiling I'm facing failures and I don't know why.
The main class
GidiyorTest.java
public class GidiyorTest {
protected WebDriver driver;
protected String baseUrl;
private boolean acceptNextAlert = true;
private StringBuffer verificationErrors = new StringBuffer();
static GidiyorTest gittiGidiyor = new GidiyorTest();
static String generatedMail = gittiGidiyor.generateString();
static String generatedUsername = gittiGidiyor.generateString();
static RegisterPage registerPage = new RegisterPage();
static LoginPage loginPage = new LoginPage();
static SearchPage searchPage = new SearchPage();
static DiscountsPage discountsPage = new DiscountsPage();
public String generateString(){
char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < 7; i++) {
char c = chars[random.nextInt(chars.length)];
sb.append(c);
}
String output = sb.toString();
return output;
}
#Before
public void setUp() throws Exception {
driver = new FirefoxDriver();
baseUrl = "https://www.gittigidiyor.com/";
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#Test
public void testGidiyor() throws Exception {
registerPage.Register();
loginPage.Login();
searchPage.Search();
discountsPage.Discount();
}
#After
....
RegisterPage.java (One of the four new classes for instance sharing just one)
public class RegisterPage extends GidiyorTest {
public void Register() throws InterruptedException {
driver.get(baseUrl + "/kayit-ol");
driver.findElement(By.name("name")).clear();
driver.findElement(By.name("name")).sendKeys("murat");
driver.findElement(By.name("surname")).clear();
driver.findElement(By.name("surname")).sendKeys("yilmaz");
Thread.sleep(300);
driver.findElement(By.id("suggestion_email_input_verifier")).clear();
driver.findElement(By.id("suggestion_email_input_verifier")).sendKeys(
generatedMail + "#gmail.com");
driver.findElement(By.id("nickname")).clear();
driver.findElement(By.id("nickname")).sendKeys(generatedUsername);
Thread.sleep(300);
driver.findElement(By.name("passwd")).clear();
driver.findElement(By.name("passwd")).sendKeys("123456abc");
driver.findElement(By.name("passwd2")).clear();
driver.findElement(By.name("passwd2")).sendKeys("123456abc");
Thread.sleep(300);
driver.findElement(By.id("cepgsm")).clear();
driver.findElement(By.id("cepgsm")).sendKeys("531");
driver.findElement(By.id("cep")).clear();
driver.findElement(By.id("cep")).sendKeys("600 29 79");
Thread.sleep(1000);
driver.findElement(By.id("SubmitForm")).click();
}
}
And the error is beginning at registerPage.Register(); line. One another is java.lang.NullPointerException.
Hope you can help.

The way you're creating your PageObject is not correct. You should not extend the test, one of the main points is that the PageObject should not know anything about the test, rather just expose the services offered by the page.
On the other hand your test should hold the assertions and other test related logic.
The second wrong thing is that you should use PageFactory to instantiate your page object, so that you can take advantage of the lazy binding mechanism. So change to something like this
public class RegisterPage {
private WebDriver driver;
public RegisterPage(WebDriver driver) {
this.driver = driver;
}
// The rest of your class
}
and instantiate inside the test using PageFactory
PageFactory.initElements(driver, RegisterPage.class);
also to ease up maintainence and benefit from lazy element binding you can think about adding your elements as fields, and mark them via annotation, so they get populated by PageFactory as well e.g.
public class RegisterPage {
private WebDriver driver;
public RegisterPage(WebDriver driver) {
this.driver = driver;
}
#FindBy(name = "name")
private WebElement name;
...
}
}

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 return new object of Generic Type with parameter driver in Page Object Model

I use Selenium.
I have Page Object like this:
public class Portal extends Utils{
private WebDriver driver;
private final By getReason= By.xpath("//a[contains(text(),'Get Sol')]");
public Portal(WebDriver driver) {
super(driver);
this.driver = driver;
}
public VisitReason clickCurrentReason() {
clickIn(getReason);
return new VisitReason(driver);
}
}
I would like this method: clickCurrentReason() return new Object extend Utils class and passed it parameter: driver.
How to do it?
I know I have to use generics. I found part of solution:
public<T extends Utils> T clickCurrentReason(){
clickIn(getReason);
return (T)(driver);
}
But how passed in return: "return new Object(driver)"
#Test method:
public void Test() {
TestingEnv testingEnv = new TestingEnv(driver);
Portal portal = testingEnv.openPage();
VisitReason visitReason = portal.clickCurrentReason();
//sometimes instead of the last line it will be: VisitInSpot visitInSpot = portal.clickCurrentReason();
//sometimes instead of the last line it will be: VisitBack visitBack = portal.clickCurrentReason();
}

Java Selenium web Element Variable vs Web Element Array

could someone tell me why if I run the following code I don´t get any error:
private WebElement userNameTxt ;
public RegisterPage(WebDriver driver) {
this.driver = driver;
userNameTxt = By.id("user_name");
}
public void registerElement() {
WebElement user = driver.findElement(userNameTxt);
}
but if I run this code I get Java.nullpointerException?
private ArrayList<WebElement> registerElements;
private WebElement userNameTxt ;
public RegisterPage(WebDriver driver) {
this.driver = driver;
ArrayList <WebElement>registerElements = new ArrayList();
userNameTxt = By.id("user_name");
}
public void registerElement() {
registerElements.add(0, driver.findElement(userNameTxt))
}
You’re doing name shadowing in your constructor, so it’s not actually assigning a value to the private class variable as you expect.
Remove the type declaration so you just have this.registerElements = new ArrayList()

How to restart browser after test when using data providers

Im currently using a Data Provider to enter numerous different users, but when the tests ends then the previous user is still logged in, how do make the bowser reset after each run? Heres my code:
public class login_from_login_page extends ConditionsWebDriverFactory {
public static final int TEST_CASE_PASSED_STATUS = 1;
public static final int TEST_CASE_FAILED_STATUS = 5;
#Test(dataProviderClass = Data.DataProviders.class, dataProvider = "customer")
public void LoginActionTest (String pass, String email, String user) throws Exception{
Header header = new Header();
header.guest_select_login();
Pages.Login login = new Pages.Login();
login.LoginAction(pass, email, user);
//TestRail.addResultForTestCase("16870",TEST_CASE_PASSED_STATUS," ");
//TestRail.addResultForTestCase("16874",TEST_CASE_PASSED_STATUS," ");
//TestRail.addResultForTestCase("17199",TEST_CASE_PASSED_STATUS," ");
}
}
I have a Webdriverfactory class that is extended from my test class here but its doesn't seem to be creating a new instance, im always still logged in when it restarts using the new DataProvider information.
#Listeners({ScreenShotOnFailListener.class})
public class ConditionsWebDriverFactory {
#BeforeClass
public void beforeTest() {
Drivers.startBrowser(true);
Drivers.getDriver().manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
}
#AfterClass (alwaysRun = true)
public void afterTest() {
Drivers.finishBrowser();
}
}
Start your test with initialization before test execution:
#BeforeMethod
void setupTest() {
WebDriver driver = new ChromDriver();
// method code here
}
Alternatively initialize your page object with new driver instance:
Pages.Login login = new Pages.Login(new ChromDriver());
Another alternative:
#Test
void seleniumTest() {
// Test code here
login.getDriver().manage().deleteAllCookies();
login.getDriver().navigate.refresh();
}
You can add after method, if you use testng:
#AfterMethod
void deleteCoockies() {
Drivers.getDriver().manage().deleteAllCookies();
Drivers.getDriver().get ("homePageUrl");
}

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