Within a Test Suite, Objects that model the Webpage are defined with a Java class file, which then gets utilized in Test Scripts. Those test scripts are ran within the Test Suite itself, which is called upon an Automation Library, and can be used to generate Reports for developers to read.
Java Generics are a language feature that allows for definition and use of generic types and methods.
ExampleClass
with a defined constructor that utilizes ExampleClass<E>
where the class uses type parameter E in creating the class instance. The type "E" is called a type argument, and allows classes to instantiate as a parameterized type denoted by "E." A basic example would be LinkedList<Integer>
, that defines a linked list that can hold Integer objects.The Page class will be used to declare all class members that every Web Page needs. The Base Page class will define behavior that a user would perform on a Web Page, which will be utilized by the Page Objects that inherit from Base Page. The POM classes themselves will define methods that should be unique for the Web Page that is ideally on the same domain of where we are testing.
> mvn --version
<dependencies> <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -→ <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>3.141.59</version> </dependency> <!-- https://github.com/bonigarcia/webdrivermanager -→ <dependency> <groupId>io.github.bonigarcia</groupId> <artifactId>webdrivermanager</artifactId> <version>4.3.1</version> </dependency> <!-- https://mvnrepository.com/artifact/org.testng/testng -→ <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>7.3.0</version> </dependency> <!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-testng -→ <dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-testng</artifactId> <version>6.9.1</version> </dependency> <!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-java -→ <dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-java</artifactId> <version>6.9.1</version> </dependency> <!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-core -→ <dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-core</artifactId> <version>6.10.0</version> </dependency> </dependencies>
public class Page { //A Page has a Web Driver and a WebDriverWait to define timeouts protected WebDriver driver; protected WebDriverWait wait; //Standard Constructor public Page(WebDriver driver, WebDriverWait wait){ this.driver = driver; this.wait = wait; } //Standard Getters and Setters just in case public WebDriver getDriver() { return this.driver; } public void setDriver(WebDriver driver) { this.driver = driver; } }
public <T extends BasePage> T getInstance (Class<T> pageClass) { try { return pageClass.getDeclaredConstructor(WebDriver.class, WebDriverWait.class).newInstance(this.driver, this.wait); } catch (Exception e) { e.printStackTrace(); return null; } }There are two cases of Generics being used: the return type of the function and as part of the parameter that the method will except. Let us break down what we see here in this section.
public abstract class BasePage extends Page { //Super Constructor called; this will repeat for all other children of BasePage from here on out protected BasePage (WebDriver driver, WebDriverWait wait) { super(driver, wait); } //Target a web element on the Web Page and click on it public void click (By elementLocation) { try{ wait.until(ExpectedConditions.visibilityOfElementLocated(elementLocation)); driver.findElement(elementLocation).click(); } catch(StaleElementReferenceException e){ driver.findElement(elementLocation).click(); } } //Target a web element on the Web Page and write text to it if able public void writeText (By elementLocation, String text) { try{ wait.until(ExpectedConditions.visibilityOfElementLocated(elementLocation)); driver.findElement(elementLocation).sendKeys(text); } catch(StaleElementReferenceException e){ driver.findElement(elementLocation).sendKeys(text); } } //Target a web element on the Web Page and read text from it if able public String readText (By elementLocation) { try{ wait.until(ExpectedConditions.visibilityOfElementLocated(elementLocation)); return driver.findElement(elementLocation).getText(); } catch(StaleElementReferenceException e){ return driver.findElement(elementLocation).getText(); } } //Target a web element on the Web Page and send the "Tab" key (mostly used to navigate input fields) public void sendTabKey(By elementLocation){ try{ wait.until(ExpectedConditions.visibilityOfElementLocated(elementLocation)); driver.findElement(elementLocation).sendKeys(TAB); } catch(StaleElementReferenceException e){ driver.findElement(elementLocation).sendKeys(TAB); } } }
public class HomePage extends BasePage { //Super constructor like in BasePage public HomePage (WebDriver driver, WebDriverWait driverwait) { super(driver, driverwait); } //*********Page Variables********* private String homePageURL = "https://formy-project.herokuapp.com/"; //*********Web Elements********* private By logoID = By.id("logo"); private By autoComplete = By.xpath("//a[contains(@href,'/autocomplete') and contains(@class,'btn btn-lg')]"); private By buttons = By.xpath("//a[contains(@href,'/buttons') and contains(@class,'btn btn-lg')]"); private By checkBox = By.xpath("//a[contains(@href,'/checkbox') and contains(@class,'btn btn-lg')]"); private By datepicker = By.xpath("//a[contains(@href,'/datepicker') and contains(@class,'btn btn-lg')]"); private By modal = By.xpath("//a[contains(@href,'/modal') and contains(@class,'btn btn-lg')]"); public String getHomePageURL() { return this.homePageURL; } public void navigateToHome(){ click(logoID); } public HomePage navigateBack(){ driver.navigate().back(); return this; } public HomePage clickAutoComplete(){ click(autoComplete); return this; } public HomePage clickButtons(){ click(buttons); return this; } public HomePage clickCheckBox(){ click(checkBox); return this; } public HomePage clickDatepicker(){ click(datepicker); return this; } public HomePage clickModal(){ click(modal); return this; } public HomePage goToFormyProject() { driver.get(homePageURL); return this; } }
public class AutoCompletePage extends BasePage { //Super constructor like in BasePage public AutoCompletePage (WebDriver driver, WebDriverWait wait) { super(driver, wait); } //*********Web Elements********* private By logoID = By.id("logo"); private By addressTextField = By.id("autocomplete"); private By streetAddressTextField = By.id("street_number"); private By streetAddress2TextField = By.id("route"); private By cityTextField = By.id("locality"); private By stateTextField = By.id("administrative_area_level_1"); private By zipCodeTextField = By.id("postal_code"); private By countryTextField = By.id("country"); public void navigateToHome(){ click(logoID); } public AutoCompletePage clickAddressTextField(){ click(addressTextField); return this; } public AutoCompletePage clickStreetAddressTextField(){ click(streetAddressTextField); return this; } public AutoCompletePage clickStreetAddress2TextField(){ click(streetAddress2TextField); return this; } public AutoCompletePage clickCityTextField(){ click(cityTextField); return this; } public AutoCompletePage clickStateTextField(){ click(stateTextField); return this; } public AutoCompletePage clickZipCodeTextField(){ click(zipCodeTextField); return this; } public AutoCompletePage clickCountryTextField(){ click(countryTextField); return this; } public AutoCompletePage switchToStreetAddressTextField(){ sendTabKey(addressTextField); return this; } public AutoCompletePage switchToStreetAddress2TextField(){ sendTabKey(streetAddressTextField); return this; } public AutoCompletePage switchToCityTextField(){ sendTabKey(streetAddress2TextField); return this; } public AutoCompletePage switchToStateTextField(){ sendTabKey(stateTextField); return this; } public AutoCompletePage switchToZipCodeTextField(){ sendTabKey(zipCodeTextField); return this; } public AutoCompletePage switchToCountryTextField(){ sendTabKey(countryTextField); return this; } public AutoCompletePage enterAddress(String address){ writeText(addressTextField,address); return this; } public AutoCompletePage enterStreetAddress(String streetAddress) { writeText(streetAddressTextField,streetAddress); return this; } public AutoCompletePage enterStreetAddress2(String streetAddress2) { writeText(streetAddress2TextField,streetAddress2); return this; } public AutoCompletePage enterCity(String city) { writeText(cityTextField,city); return this; } public AutoCompletePage enterState(String state) { writeText(stateTextField,state); return this; } public AutoCompletePage enterZipCode(String zipCode) { writeText(zipCodeTextField,zipCode); return this; } public AutoCompletePage enterCountry(String country) { writeText(countryTextField,country); return this; } }
public class CheckboxesPage extends BasePage { //Super constructor like in BasePage public CheckboxesPage (WebDriver driver, WebDriverWait wait) { super(driver, wait); } //*********Web Elements********* private By logoID = By.id("logo"); private By checkBox1 = By.cssSelector("input#checkbox-1"); private By checkBox2 = By.cssSelector("input#checkbox-2"); private By checkBox3 = By.cssSelector("input#checkbox-3"); public void navigateToHome(){ click(logoID); } public CheckboxesPage clickCheckBox1(){ click(checkBox1); return this; } public CheckboxesPage clickCheckBox2(){ click(checkBox2); return this; } public CheckboxesPage clickCheckBox3(){ click(checkBox3); return this; } public boolean verifyAllCheckedBoxes(){ boolean verify = false; List<WebElement> uncheckboxes = driver.findElements(By.cssSelector("input:not(:checked)[type='checkbox']")); if(uncheckboxes.isEmpty()){ verify = true; } return verify; } }
public class DatepickerPage extends BasePage { //Super constructor like in BasePage public DatepickerPage (WebDriver driver, WebDriverWait wait) { super(driver, wait); } //*********Web Elements********* private By logoID = By.id("logo"); private By datepickerTextField = By.id("datepicker"); private String period = "datepicker-switch"; private String left = "prev"; private String right = "next+"; private String dayFORMAT = "//td[contains(text(),%d) and contains(@class, \"day\")]"; public void navigateToHome(){ click(logoID); } public DatepickerPage clickDatePickerTextField(){ click(datepickerTextField); return this; } public LocalDate getCurrentPeriod(){ By calenderHeader = By.className(period); var currentPeriod = driver.findElement(calenderHeader).getText().split(" "); return LocalDate.of( Integer.parseInt(currentPeriod[1]), Month.valueOf(currentPeriod[0].toUpperCase()), 1); } public void chooseMonth(LocalDate date) { var currentPeriod = getCurrentPeriod(); By leftArrow = By.className(left); By rightArrow = By.className(right); long monthsAway = ChronoUnit.MONTHS.between(currentPeriod, date.withDayOfMonth(1)); By arrow = monthsAway < 0 ? leftArrow : rightArrow; for(int i = 0; i < Math.abs(monthsAway); i++){ click(arrow); } } public void chooseDay(int dayOfMonth) { By locator = By.xpath(format(dayFORMAT, dayOfMonth)); click(locator); } public LocalDate chooseDate(LocalDate date){ chooseMonth(date); chooseDay(date.getDayOfMonth()); return getSelectedDate(); } public LocalDate getSelectedDate(){ var fields = driver.findElement(datepickerTextField).getAttribute("value").split("/"); return LocalDate.of( Integer.parseInt(fields[2]), Integer.parseInt(fields[0]), Integer.parseInt(fields[1])); } }
public class ModalPage extends BasePage { //Super constructor like in BasePage public ModalPage (WebDriver driver, WebDriverWait wait) { super(driver, wait); } //*********Web Elements********* private By logoID = By.id("logo"); private By modalButtonID = By.id("modal-button"); private By okButtonID = By.id("ok-button"); private By closeButtonID = By.id("close-button"); private By xButtonID = By.cssSelector("[aria-hidden=true]"); public void navigateToHome(){ click(logoID); } public ModalPage clickModalButton(){ click(modalButtonID); return this; } public ModalPage clickOkButton(){ click(okButtonID); return this; } public ModalPage clickCloseButton(){ click(closeButtonID); return this; } public ModalPage clickXButton(){ click(xButtonID); return this; } }
public class HerokuAutoCompleteTests { private WebDriver driver; private WebDriverWait wait; private Page page; @BeforeMethod @Parameters("browser") public void setup(String browser) throws Exception { if (browser.equalsIgnoreCase("chrome")) { WebDriverManager.chromedriver().setup(); ChromeOptions options = new ChromeOptions (); options.setPageLoadStrategy(PageLoadStrategy.EAGER); driver = new ChromeDriver (options); } else if (browser.equalsIgnoreCase("edge")) { WebDriverManager.edgedriver().setup(); driver = new EdgeDriver (); } else if (browser.equalsIgnoreCase("firefox")) { WebDriverManager.firefoxdriver().setup(); driver = new FirefoxDriver (); } else{ //If no browser passed throw exception throw new Exception("Browser is not correct"); } //Create a wait. All test classes use this. wait = new WebDriverWait (driver,20); //Maximize Window driver.manage().window().maximize(); driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS); //Instantiate the Page Class page = new Page(driver,wait); } @Test (priority = 0, groups = { "heroku"}) public void autoPageFormEnterText() { page.getInstance(HomePage.class).goToFormyProject() .clickAutoComplete(); page.getInstance(AutoCompletePage.class).enterAddress("6712 Kingsbury Dr.") .switchToStreetAddressTextField() .enterStreetAddress("Ultra Testing 123") .switchToStreetAddress2TextField() .enterStreetAddress2("Testing out the enter text function") .switchToCityTextField() .enterCity("Dallas") .switchToStateTextField() .enterState("Texas") .switchToZipCodeTextField() .enterZipCode("60842") .switchToCountryTextField() .enterCountry("United States"); } @AfterMethod public void teardown () { driver.quit(); } }
Feature: Heroku Applications and Webpages * This is a basic demonstration of utilizing Cucumber and TestNG * to highlight the useage of these technologies and a tutorial * for someone to follow along Scenario: Verify the functionality of the Checkbox Page Given I am on the Checkbox Page When I click on all checkboxes Then all the checkboxes will be active Scenario: Verify the behavior of the Modal Pagetitle Given I am on the Modal Page When I open the modal on the webpage Then I can close it with the close button Scenario_Outline: Verify that the Date Picker can choose several dates Given I am on the Datepicker Page When I click on the text field Then I can select a date with <day>, <month>, and <year> And the full date should display on the text field Examples: | day | month | year | | 16 | 10 | 2010 | | 03 | 8 | 2015 | | 24 | 5 | 2000 |
public class StepDefinition { private WebDriver driver; private WebDriverWait wait; private Page page; private LocalDate date; @Before public void setup() throws Exception { System.out.println("*******before SanitySmoke*******"); String browser = "chrome"; if (browser.equalsIgnoreCase("chrome")) { WebDriverManager.chromedriver().setup(); ChromeOptions options = new ChromeOptions (); options.setPageLoadStrategy(PageLoadStrategy.EAGER); driver = new ChromeDriver (options); } else if (browser.equalsIgnoreCase("edge")) { WebDriverManager.edgedriver().setup(); driver = new EdgeDriver (); } else if (browser.equalsIgnoreCase("firefox")) { WebDriverManager.firefoxdriver().setup(); driver = new FirefoxDriver (); } else{ //If no browser passed throw exception throw new Exception("Browser is not correct"); } //Create a wait. All test classes use this. wait = new WebDriverWait (driver,20); //Maximize Window driver.manage().window().maximize(); driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS); //Instantiate the Page Class page = new Page(driver,wait); } @Given("^I am on the Checkbox Page$") public void i_am_on_the_checkbox_page() { page.getInstance(HomePage.class).goToFormyProject() .clickCheckBox(); } @When("^I click on all checkboxes$") public void i_click_on_all_checkboxes() { page.getInstance(CheckboxesPage.class).clickCheckBox1() .clickCheckBox2() .clickCheckBox3(); } @Then("^all the checkboxes will be active$") public void all_the_checkboxes_will_be_active(){ boolean result = page.getInstance(CheckboxesPage.class).verifyAllCheckedBoxes(); System.out.println("Confirmed all checkboxes clicked:" + result); Assert.assertTrue(result); } @Given("^I am on the Modal Page$") public void i_am_on_the_modal_page() { page.getInstance(HomePage.class).goToFormyProject() .clickModal(); } @When("^I open the modal on the webpage$") public void i_open_the_modal_on_the_webpage() { page.getInstance(ModalPage.class).clickModalButton(); } @Then("^I can close it with the close button$") public void i_can_close_it_with_the_close_button(){ page.getInstance(ModalPage.class).clickCloseButton(); } @Given("^I am on the Datepicker Page$") public void i_am_on_the_datepicker_page() { page.getInstance(HomePage.class).goToFormyProject() .clickDatepicker(); } @When("^I click on the text field$") public void i_click_on_the_text_field() { page.getInstance(HomePage.class).goToFormyProject() .clickDatepicker(); } @Then("I can select a date with {int}, {int}, and {int}") public void i_can_select_a_date_with_and(int day, int month, int year) { LocalDate expectedDate = LocalDate.of(year, month, day); page.getInstance(DatepickerPage.class).clickDatePickerTextField(); date = page.getInstance(DatepickerPage.class).chooseDate(expectedDate); } @And("^the full date should display on the text field$") public void the_full_date_should_display_on_the_text_field(){ LocalDate textField = page.getInstance(DatepickerPage.class).getSelectedDate(); System.out.println(textField); Assert.assertEquals(date, textField); } @After public void tearDown(){ driver.close(); } }
@CucumberOptions( plugin = {"pretty", "html:target/cucumber-reports-regular/cucumber.html", "json:target/cucumber-json/cucumber.json" }, features = {"src/test/resources/features"}, glue = {"com.ultranauts.cucumber"} ) public class TestRunner extends AbstractTestNGCucumberTests { }
<!--?xml version="1.0" encoding="UTF-8"?-→ <suite name="Practice Suite"> <test name="Firefox Test"> <parameter name="browser" value="firefox" /> <groups> <run> <exclude name="heroku" /> <!-- change to include if test should be run-→ </run> </groups> <classes> <class name="com.ultranauts.pagetests.HerokuHomePageTests"/> <class name="com.ultranauts.pagetests.HerokuAutoCompleteTests"/> <class name="com.ultranauts.pagetests.HerokuCheckedBoxesTests"/> <class name="com.ultranauts.pagetests.HerokuDatepickerPageTests"/> </classes> </test> <!-- Test -→ <test name="MS Edge Test"> <parameter name="browser" value="edge" /> <groups> <run> <exclude name="heroku" /> <!-- change to include if test should be run-→ </run> </groups> <classes> <class name="com.ultranauts.pagetests.HerokuHomePageTests"/> <class name="com.ultranauts.pagetests.HerokuAutoCompleteTests"/> <class name="com.ultranauts.pagetests.HerokuCheckedBoxesTests"/> <class name="com.ultranauts.pagetests.HerokuDatepickerPageTests"/> </classes> </test> <test name="Chrome Test"> <parameter name="browser" value="chrome" /> <groups> <run> <exclude name="heroku" /> <!-- change to include if test should be run-→ </run> </groups> <classes> <class name="com.ultranauts.pagetests.HerokuHomePageTests"/> <class name="com.ultranauts.pagetests.HerokuAutoCompleteTests"/> <class name="com.ultranauts.pagetests.HerokuCheckedBoxesTests"/> <class name="com.ultranauts.pagetests.HerokuDatepickerPageTests"/> </classes> </test> <!-- Test -→ <test name="Regression Tests"> <classes> <class name="com.ultranauts.cucumber.TestRunner"/> </classes> </test> </suite> <!-- Suite -→
<plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M5</version> <configuration> <testFailureIgnore>true</testFailureIgnore> <systemPropertyVariables> true </systemPropertyVariables> <suiteXmlFiles> <suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile> </suiteXmlFiles> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <version>3.0.0</version> <executions> <execution> <phase>generate-sources</phase> <goals> <goal>add-source</goal> </goals> <configuration> <sources> <source>src/main/another-src</source> </sources> </configuration> </execution> </executions> </plugin> <plugin> <groupId>net.masterthought</groupId> <artifactId>maven-cucumber-reporting</artifactId> <version>2.8.0</version> <executions> <execution> <id>execution</id> <phase>verify</phase> <goals> <goal>generate</goal> </goals> <configuration> <projectName>Heroku Cucumber JVM Report</projectName> <outputDirectory>${project.build.directory}/cucumber-JVM</outputDirectory> <cucumberOutput>${project.build.directory}/cucumber-json/cucumber.json</cucumberOutput> <enableFlashCharts>true</enableFlashCharts> <checkBuildResult>false</checkBuildResult> </configuration> </execution> </executions> </plugin> <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -→ <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -→ <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <release>15</release> </configuration> </plugin> <plugin> <artifactId>maven-jar-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -→ <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> </plugin> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> </plugins>
> git clone https://github.com/austin-ultratesting/JavaUICapstoneProject OPTIONAL_DIRECTORY_NAME
I | Attachment | Action | Size | Date | Who | Comment |
---|---|---|---|---|---|---|
png | Advanced POM Model.png | manage | 39 K | 17 Feb 2021 - 05:35 | AustinBell | Intended goal for writing up classes for POMs to inherit from their Parent Classes |
png | Failure report.png | manage | 92 K | 19 Feb 2021 - 19:38 | AustinBell | Example report from the Cucumber Test covering failure overview |
png | Features report.png | manage | 81 K | 19 Feb 2021 - 19:37 | AustinBell | Example report from the Cucumber Test covering feature overview |
png | POM Model.png | manage | 40 K | 16 Feb 2021 - 10:01 | AustinBell | POM Model Diagram for the Wiki Entry |
txt | feature-overview.html.txt.txt.txt | manage | 1 byte | 01 Jan 1970 - 00:00 | UnknownUser | Example report from the Cucumber Test covering feature overview |