JUnit/Selenium: Splitting my class to use the Page Object Model - java

realise this might be a bit of a big ask, but I'm having trouble splitting my test class to use the Page Object Model.
My current class basically works as so:
#BeforeClass - startUp() Loads properties file for variables and starts ChromeDriverService
#Before - createDriver() Opens Chrome browser, and navs to homepage
#Test - desktopHappyCallback() Does some page interaction, then calls:
hideDynamicElements() Hides some elements on the page
runScreenshotValidation() Saves a screenshot and compares it by calling:
compareScreen() Compares and returns a true/false
#After - tearDown() Quits the ChromeDriver
#AfterClass - stopService() Kills the ChromeDriverService
The class is currently (have cut bits out of the methods to make it shorter):
public class desktopHappy {
private static ChromeDriverService service;
private static WebDriver driver;
private static Properties obj = new Properties();
//Set up screenshot comparison via im4java
private static boolean compareScreen(String expectedImage, String currentImage, String diffImage) {
CompareCmd compare = new CompareCmd();
compare.setErrorConsumer(StandardStream.STDERR);
IMOperation comparisonExe = new IMOperation();
comparisonExe.metric("mae"); //mae = Mean Absolute error (average of all the color differences)
comparisonExe.addImage(currentImage);
}
//Compare via ImageMajick
private void runScreenshotValidation() throws IOException, InfoException {
String current = "screenshots\\current\\Current.png";
boolean compareSuccess = compareScreen(baseline, current, difference); //Run the compare method
}
}
//Hide dynamic elements on the page (if they exist)
private void hideDynamicElements() throws IOException, InterruptedException {
List<By> criteria = Arrays.asList(
By.className("telephone-number"),
By.cssSelector("#chatContainer"),
);
for (By dynamicElement : criteria) { //Loop through the criteria
List<WebElement> elements = driver.findElements(dynamicElement);
for (WebElement hideElement : elements){ //Loop through each instance of an element
((JavascriptExecutor) driver).executeScript("arguments[0].style.visibility='hidden'", hideElement);
}
}
#BeforeClass //Pulls in Properties, and starts up the ChromeDriverService before the Tests run
public static void startUp() throws IOException {
FileInputStream propfile = new FileInputStream(System.getProperty
("user.dir") + "\\src\\main\\resources\\config.properties");
obj.load(propfile);
service = new ChromeDriverService.Builder()
.usingDriverExecutable(new File(obj.getProperty("chromeDriverLoc"))) //Driver set in properties
.usingAnyFreePort()
.build();
service.start();
}
#Before //Starts up the Chrome browser (on home page) before each Test
public void createDriver() {
driver = new RemoteWebDriver(service.getUrl(), DesiredCapabilities.chrome());
driver.get(obj.getProperty("targetENV")); //Opens home page (set in properties)
}
#Test //Checks the Callback form (modal)
public void desktopHappyCallback() throws Exception {
driver.findElement(By.cssSelector("#callbackInputPhone")).sendKeys(obj.getProperty("formsPhone"));
driver.findElement(By.xpath("//input[#value='Request a callback']")).click();
WebDriverWait waitForSuccessMessage = new WebDriverWait(driver, 5);
waitForSuccessMessage.until(ExpectedConditions.elementToBeClickable
(By.cssSelector("#callbackForm > div.success-message")));
hideDynamicElements(); //Run the hide dynamic elements method
runScreenshotValidation(); //Run the screenshot comparison method
}
#After //Quits the ChromeDriver after each Test
public void tearDown() throws Exception {
driver.quit();
}
#AfterClass //Kills the ChromeDriverService at end of Tests
public static void stopService() {
service.stop();
}}
So in IntelliJ I've tried to split up my project with packages as so:
I've been trying to move into the baseCompare class:
hideDynamicElements()
compareScreen()
runScreenshotValidation()
Then into the baseSetup class:
startUp()
createDriver()
tearDown()
stopService()
Then homePage will contain my representations and desktopHappy will contain my test assertions.
However I'm getting really stuck working out how to split things up so that they work together. Is anyone able to give me an example on how I should be splitting things up under these packages? (sorry big ask!)

I think you are dividing your logic very fairly.
The only thing that i would change, is to have your base* classes under your base package, rather than an actual physical base package.
For example:
com.salessite/
baseCompare.java
baseSetup.java
basePage.java
baseTest.java
pages/
homePage.java < basePage.java
tests/
desktopHappy.java < baseTest.java
Also by creating more base's, you give yourself more leeway for future changes.
so that they work together
IF by work you mean, compile - then you should have no problem. say for example that homePage.java extends basePage.java, then in homePage.java, you'd have the appropriate import.
import com.salessite.basePage;
Importing is how to access external classes and references. (access modifiers permitting, of course.)

Related

Java automation selenium convert to Pom or factory

I need help with creating Java automation with Selenium Webdriver to Pom or Pomm factory.
I've read how to create pom without any success.
Please help.
I need help on how to create java automation in pom. strong textHow to conver it?
String baseUrl = "https:amazon.com/";
WebDriver driver;
NavigationPom navigationPom;
private final boolean useFirefoxbrowser = false;
#BeforeClass
public static void setupClass() {
WebDriverManager.chromedriver().setup();
WebDriverManager.firefoxdriver().setup();
}
#Before
public void setUp() {
if (useFirefoxbrowser == false) {
FirefoxOptions firefoxOptions = new FirefoxOptions();
firefoxOptions.addArguments("--width=1240", "--height=720");
driver = new FirefoxDriver(firefoxOptions);
} else {
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments("--window-size=1920,1080");
driver = new ChromeDriver(chromeOptions);
}
}
#Test
public void MacbookTest1() {
driver.get(baseUrl);
driver.manage().window().maximize();
driver.findElement(By.xpath("//input[#id='twotabsearchtextbox']")).click();
driver.findElement(By.xpath("//input[#id='twotabsearchtextbox']")).sendKeys("macbook");
driver.findElement(By.xpath("//input[#id='nav-search-submit-button']")).click();
driver.findElement(By.xpath("//input[#id='nav-search-submit-button']")).click();
driver.findElement(By.xpath("//li[#id='p_89/Lenovo']/span/a/div/label/i")).click();
//Checkboxes
boolean enabled = driver.findElement(By.xpath("//li[#id='p_89/Lenovo']/span/a/div/label/i")).isEnabled();
Start from creating a simple implementation:
How the Page class might look:
class Page {
private WebDriver driver;
public Page(WebDriver driver) {
this.driver.driver = driver;
}
//Define locators
//Keep elements as By on the top of the class
private By someElement1 = By.xpath("...");
private By someElement2 = By.xpath("...");
//Define page methods
// Examples:
// public void clickSomeElement()
// public void fillSomeInput()
public void doSomeAction() {
WebElement element = driver.findElement(someElement1);
// do something with the element
// optionaly wait for something after action
}
// Examples:
// public boolean isPageLoaded()
// public boolean isSomeElementDisplayed()
// public String getSomeElementText()
// public int getSomeElementsCount()
public Object getSomeData() {
WebElement element = driver.findElement(someElement2);
// do something with the element and return
}
}
Use pages in your tests, do not work with WebDriver directly.
#Test
public void someTest() {
Page page = new Page(driver);
page.doStep1();
page.doStep2();
assertEquals(page.getSomeData(), "Some expected result", "Optional Error message");
}
And..
Start from creating a test scenario code, create test steps, which you like to have (methods, which will be underlined in IDE as non-existing), and think about the data, which you like to check.
Then just implement all the non-existing methods you need in Page classes.
Maybe it sounds complicated, but try to start from simple test scenarios and it should become more clear after some practice.
I had the same issue. If this is a Java project, with Eclipse and Intellij IDEA, you can right click the project and select Convert to Maven. From there, it's fine adjustments (versions) in the POM file.

How to run tests without closing webdriver using PO?

Please help me with my Selenium project.
Link for GIT repository: https://kodov#bitbucket.org/kodov/addressbook5.git
I have an application Addressbook ( https://sourceforge.net/projects/php-addressbook/ ) which I want to test.
I made several tests for Login page and page for creating a new contact.
The case is to make:
Negative test for login
Positive test for login
Then I don't need to close the browser but to run tests for creating a contact.
The problem is that I made POM and my tests and pages are in different classes, so I don't know how to quit the
Webdriver just after all tests, but not after the first one.
Maybe I need to change annotations.
You can create a Base Test class and make the other tests class extend that class.
For example:
public class BaseTest {
public static WebDriver driver;
private static boolean isTestsStarted;
public static WebDriverWait wait;
#BeforeClass
public static void setup() {
// To run setup method before all tests that extends this base class
if(!isTestsStarted) {
System.setProperty("webdriver.chrome.driver", ConfProperties.getProperty("chromedriver_path"));
driver = new ChromeDriver();
wait = new WebDriverWait(driver, Duration.ofSeconds(10));
driver.get(ConfProperties.getProperty("base_url"));
isTestsStarted = true;
}
}
// To quit driver after all tests run
// This will run only once, after all tests ran
// Another advantage of this is, it will quit driver even though you stop the program manually
static {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
driver.quit();
}));
}
}
Then extend this base class in your test classes
for your loginTest class
public class LoginTest extends BaseTest {
public static LoginPage loginPage;
public static MainPage mainPage;
#BeforeClass
public static void setup() {
// You can call driver object since you initialized it in your parent class
loginPage = new LoginPage(driver);
mainPage = new MainPage(driver);
driver.get(ConfProperties.getProperty("base_url"));
}
#Test
public void testUserCanNotLoginWithInvalidCredentials() {
loginPage.clearUsernameField().clearPasswordField()
.enterUsername(ConfProperties.getProperty("invalid_username"))
.enterPassword(ConfProperties.getProperty("invalid_password"))
.clickLoginButton();
assertThat(loginPage.getLoginButton().isDisplayed()).isTrue();
}
#Test
public void testUserCanLoginWithValidCredentials() {
loginPage.clearUsernameField().clearPasswordField()
.enterUsername(ConfProperties.getProperty("valid_username"))
.enterPassword(ConfProperties.getProperty("valid_password"))
.clickLoginButton();
assertThat(mainPage.getUserName())
.isEqualTo(ConfProperties.getProperty("valid_username"));
}
}
With the same way, you can use your driver object in other classes if you make those classes extend BaseTest class
You need to login no matter what, so you have to include the login part in both of your tests.
You can also try #Test(dependsOnMethods = { "testName" }) but I am not sure not if this works when your test is in another file.

When trying to run Selenium Cucumber test in two browsers, test runs in only one browser

I am stuck whilst trying to run a selenium cucumber java script in two browsers (Chrome, Firefox). The test works fine when I use a single browser. I use selenium PageFactory class to initialise the web elements.
The problem is when I run the test for two browsers, the first browser opens, navigates to the URL and then nothing happens. Web elements are not initialised. The test moves on to the second browser, navigates to URL, web elements are initialised, subsequent test methods (testMethod1) run as expected. Why is the test not running on the first browser?
This is the PageFactory BasePage class holding the web elements:
public class BasePage {
private final WebDriver driver;
public BasePage(WebDriver driver) {this.driver = driver;} //constructor
#FindBy(id = "cc-amount")
public WebElement amountField;
This is the test class and how I have tried to run the test in two browsers:
public class Convert {
private static WebDriver driver;
private final BaseUtil baseUtil = new BaseUtil();
private static BasePage basePage;
private static int browser;
public void navigateToUrl(String url) throws InterruptedException {
for (browser = 1; browser <= 2; browser++) {
if (browser == 1) {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
} else if (browser == 2) {
WebDriverManager.firefoxdriver().setup();
driver = new FirefoxDriver();
}
driver.get(baseUtil.getMyUrl()); //Url coming from a utility class
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
basePage = PageFactory.initElements(driver, BasePage.class); //initialisation of the BasePage class conttaining the pagefactory web elements
After the browser initialisation, this method runs ok in the second browser (Firefox) but did not run at all in the first browser (Chrome):
public void testMetod1(String amount) throws InterruptedException {
basePage.amountField.click();
My suspicion is that PageFacory could not be initiated for both browsers in a single run but I do not know how to diagnose this further. It could also be a for loop error.
I believe it's a synchronisation issue. Just you need to create a class for each browser, then you can create another base class which contains all the shared code.
Creating a class for each browser will give you some advantages:
Cleaner code.
Your tests will run in parallel.
Specific test cases for a specific browsers (Specially for IE).
Solve this sync issue.
So basically instead of using Converter, you can create:
ChromeConverter
FirefoxConverter
And inside your TestExecuter class, just create instance for each one and run your tests.
And to be more detailed, you are using one class Convert and inside it you have two static variables:
private static WebDriver driver;
private static BasePage basePage;
Removing the static keyword from BasePage class won't solve the root cause as you are using BasePage which is a static class and can't be instantiated as instance class and it's inside an external library so you need to separate the classes as kind of wrapper.
Here you are trying to use the same exact initialisation with Chrome then Firefox which won't work in parallel or even in for loop until you can wait to the first test then dispose it and initialise all the stuff related to the second test again.
So the correct approach is to create a separate Convert for each browser like that:
ChromeConverter
FirefoxConverter
And In your TestExecuter you just need to initialise each one and use it, something like that:
#Given("...")
public void navigateToUrl(String url) throws InterruptedException {
chromeConverterApp.navigateToUrl(url);
firefoxConverterApp.navigateToUrl(url);
}
#When("^..")
public void enterCurrencies(String amount, String from, String to) throws InterruptedException {
chromeConverterApp.enterCurrencies(amount, from, to);
firefoxConverterApp.enterCurrencies(amount,from,to);
}
The for loop opens Chrome on the first run (browser=1), then opens Firefox on the second run (browser=2) and then continues with the test on the last opened browser (Firefox).
If you wish to run the complete test on both browsers, you need to put the rest of the test inside of the for loop.
Basically, you have something like:
public void navigateToUrl(String url) throws InterruptedException {
for (browser = 1; browser <= 2; browser++) {
//body of the for loop
}
//some more code
} //end of navigateToUrl
You need to change it to:
public void navigateToUrl(String url) throws InterruptedException {
for (browser = 1; browser <= 2; browser++) {
//body of the for loop
}
//some more code
testMethod1()
} //end of navigateToUrl
Your approach is not correct, if you need to run your tests in parallel you should go for either Selenium Grid or consider implementing other multi-threading option, i.e.:
Plain Java: ExecutorService
JUnit or TestNG: Maven Surefire Plugin Fork Options and Parallel Test Execution
In any case you will need to refactor your test to follow Parallel Tests - Best Practices at least:
Remove static modifier from the WebDriver declaration
Consider putting your WebDriver instance into the Thread Local Storage otherwise you will have clashes

Is it a good practise to initiate one object per all page tests?

I'm using Selenium with Page Object Pattern. I've got a question about creating objects of page object classes.
Which option is better:
#BeforeTest
public void browser() throws IOException {
driver = initializeBrowser();
loginPage = new LoginPage(driver);
}
and using it like this:
#Test
public void loginToApp() throws InterruptedException {
loginPage.clickLoginButton();
Assert.assertTrue("some assertion");
}
#Test
public void loginToAppUsingLogin() throws IOException {
loginPage.sendLogin("login");
loginPage.sendPassword("password");
loginPage.clickLoginButton();
Assert.assertTrue("some assertion");
}
Or
#BeforeTest
public void browser() throws IOException {
driver = initializeBrowser();
}
#Test
public void loginToApp() throws InterruptedException {
loginPage = new LoginPage(driver);
loginPage.clickLoginButton();
Assert.assertTrue("some assertion");
}
#Test
public void loginToAppUsingLogin() throws IOException {
loginPage = new LoginPage(driver);
loginPage.sendLogin("login");
loginPage.sendPassword("password");
loginPage.clickLoginButton();
Assert.assertTrue("some assertion");
}
Are there any contraindications to create one object in #BeforeTest per test suite?
I don't know the what the consent is, but the #BeforeTest annotation is used correctly like you did. It initializes your loginPage object before every individual test.
(I'm assuming you use TestNG)
In my experience your first approach is better as it also reduces the amount of repeated code. See DRY
In my opinion, I think you are splitting hairs here. For me, I prefer to create a new object per test simply because it provides a 'clean' run, i.e., I am not re-using the same instance for a new test. And to be even more clear/transparent, I clear the cache on the browser each time as well.
Within each test I do this:
[Test, Order(10), Description("Navigate to the 'Dashboard' page, click the 'Open' button and fill out the form that loads.")]
public void NavigateToDashboardAndClickElement()
{
// Setup a null instance of IE for use in testing.
IWebDriver driver = null;
// Instantiate the IESetupHelper class.
IESetupHelper setupIE = new IESetupHelper();
// Set the environment variables for IE, and launch the browser.
setupIE.SetupIEVariables(driver);
}
And for setting up the browser itself, I do the following:
public void SetupIEVariables(IWebDriver driver)
{
// Set the options for the driver instance. In this case, we are ignoring the zoom level of the browswer we are going to use.
InternetExplorerOptions options = new InternetExplorerOptions { IgnoreZoomLevel = true };
// Clear the broswers cache before launching.
options.EnsureCleanSession = true;
// Create a new driver instance.
driver = new InternetExplorerDriver(#"path to driver here", options);
// Set the window size for the driver instance to full screen.
driver.Manage().Window.Maximize();
// Set the URL for the driver instance to go to.
driver.Url = #"some URL here";
}

Creating a webdriver instance across classes | selenium Java

Just pitching into Java! Trying to implement BDD style framework...
I'm running into this issue
My Driver.java looks like this:
public class Driver {
public static WebDriver Instance;
#Before
public void InitializeTest() {
System.setProperty("webdriver.chrome.driver", "C://chromedriver.exe");
Instance = new ChromeDriver();
}
#After
public void TearDownTest(Scenario scenario) {
//close the browser
if (scenario.isFailed()) { //take Screenshot
System.out.println(scenario.getName());
}
Instance.close();
}
}
And my Step-definition file:
public class MyStepdefs {
public static String Url = "https://ebay.com/staging/";
LoginPage loginPage = new LoginPage();
#Given("^I login to Ebay as \"([^\"]*)\"$")
public void iLoginToEbayAs(String username) throws Throwable {
Driver.Instance.navigate().to(Url);
loginPage.setUserName().sendKeys(username);
loginPage.setPassword().sendKeys("seeeev");
}
Receiving this error:
java.lang.NullPointerException
at Steps.MyStepdefs.iLoginToEbayAs(MyStepdefs.java:4)
MyStepdefs.java:4 == Driver.Instance.navigate().to(Url);
Help me pass through this!
Your Driver class never gets initialized, I'm betting Instance is null. I think you have at least 2 options, possibly more ways but this is what i'm thinking.
Instead of using #Before, which never gets executed because you don't have any test methods in that class, just make that a static "Init" method that you call in order to initialize your Instance variable
Make your Driver class an abstract class that your MyStepDefs class extends from. When you run your method iLoginToEbayAs() it will by default call the #Before method in the parent class and initialize your Instance variable as you expected.
Currently if you set a breakpoint in your #Before method I'm betting it's never getting executed hence NPE.
You are missing the argument in the step definition. it should be given below.
#Given("^I login to Ebay as \"([^\"]*)\"$")
public void iLoginToEbayAs(String role) throws Throwable {
Driver.Instance.navigate().to(Url);
}
have you imported the driver class?

Categories