├── .gitignore ├── README.md ├── pom.xml ├── regressions_testng.xml ├── src ├── main │ └── java │ │ ├── helper │ │ └── Browser.java │ │ └── pages │ │ ├── HomePage.java │ │ ├── SearchResultPage.java │ │ └── shared │ │ ├── Element.java │ │ ├── HeaderSection.java │ │ └── Page.java └── test │ └── java │ ├── helper │ └── TestHelper.java │ └── tests │ └── HomePageTests.java └── test-automation-java.iml /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | 24 | .idea/ 25 | target/ 26 | bbc-tests.iml 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Test Automation with Java 2 | Web test automation example project using IntelliJ IDEA Community, Java, Maven, TestNG, Selenium 3, Appium and Page Object Model (POM) 3 | 4 | ## Test Subject 5 | I've picked [www.bbc.co.uk](www.bbc.co.uk) as a test subject for no particular reason. 6 | It's is a large site and does have variety of features that we can play around with. 7 | 8 | ## Libraries and Frameworks 9 | Version for some of these can be found in the [POM](https://github.com/opantsjoha/test-automation-java/blob/master/pom.xml) file. 10 | * Selenium - Web automation 11 | * Appium - Mobile automation 12 | * Maven - Build and package management 13 | * TestNG - Test execution and Reporting 14 | 15 | ## Tools 16 | Using [IntelliJ IDEA Community](https://www.jetbrains.com/idea/) version. 17 | I've previously used Eclipse and found it to be a little bit slower and not as intuitive, you're free to try both and decide for yourself. Also there are a lot of comparison articles out there already. 18 | 19 | ## Programming Language 20 | I'm using [Java SE](http://www.oracle.com/technetwork/java/javase/downloads/index.html) version jdk1.8.0_111 and it works very well. 21 | There are a couple of good reasons to use Java: 22 | * It's been around for a while so there are a lot of solutions for most problems and if not then somebody from the community will be able to suggest an answer quickly. 23 | * It's OpenSource - there are large, well supported communities sharing free libraries. 24 | * Because of the above two points it's possible to automate (testing) checking for iOS (Web and Native), Android (Web and Native) and Web platforms. 25 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | qa.example.java 6 | test-automation-java 7 | 1.0-SNAPSHOT 8 | 9 | 10 | 11 | org.apache.maven.plugins 12 | maven-compiler-plugin 13 | 3.6.1 14 | 15 | 1.8 16 | 1.8 17 | 18 | 19 | 20 | 21 | jar 22 | 23 | test-automation-tests 24 | http://maven.apache.org 25 | 26 | 27 | UTF-8 28 | 29 | 30 | 31 | 32 | Regression 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-surefire-plugin 38 | 2.20 39 | 40 | 41 | regressions_testng.xml 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | org.seleniumhq.selenium 53 | selenium-support 54 | 3.4.0 55 | 56 | 57 | net.sourceforge.htmlcleaner 58 | htmlcleaner 59 | 2.21 60 | 61 | 62 | net.sourceforge.jexcelapi 63 | jxl 64 | 2.6.12 65 | 66 | 67 | org.apache.logging.log4j 68 | log4j-core 69 | 2.8.2 70 | 71 | 72 | io.appium 73 | java-client 74 | 4.1.2 75 | 76 | 77 | org.seleniumhq.selenium 78 | selenium-java 79 | 3.4.0 80 | 81 | 82 | org.testng 83 | testng 84 | 6.11 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /regressions_testng.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/main/java/helper/Browser.java: -------------------------------------------------------------------------------- 1 | package helper; 2 | 3 | import io.appium.java_client.remote.MobileCapabilityType; 4 | import org.openqa.selenium.WebDriver; 5 | import org.openqa.selenium.chrome.ChromeOptions; 6 | import org.openqa.selenium.firefox.FirefoxDriver; 7 | import org.openqa.selenium.firefox.FirefoxProfile; 8 | import org.openqa.selenium.remote.DesiredCapabilities; 9 | import org.openqa.selenium.remote.RemoteWebDriver; 10 | import pages.HomePage; 11 | 12 | import java.net.MalformedURLException; 13 | import java.net.URL; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | /** 17 | * Created by opantsjoha on 02/07/2017. 18 | */ 19 | public class Browser { 20 | 21 | 22 | public Browser(String browserName, String baseUrl) { 23 | setBrowser(browserName); 24 | setBaseUrl(baseUrl); 25 | Initialise(getBrowser()); 26 | } 27 | 28 | private void Initialise(String browser) { 29 | capabilities = new DesiredCapabilities(); 30 | seleniumFolderPath = System.getProperty("user.home") + "/Documents/umservices/selenium3/"; 31 | 32 | switch (browser) { 33 | case "Chrome": 34 | ChromeOptions chrome_options = new ChromeOptions(); 35 | chrome_options.addArguments("--disable-geolocation"); 36 | chrome_options.addArguments("--incognito"); 37 | System.setProperty("webdriver.chrome.driver", seleniumFolderPath + "chromedriver"); 38 | capabilities.setBrowserName("chrome"); 39 | capabilities.setCapability(ChromeOptions.CAPABILITY, chrome_options); 40 | // _driver = new ChromeDriver(capabilities); 41 | break; 42 | case "Safari": 43 | capabilities.setBrowserName("safari"); 44 | 45 | // _driver = new SafariDriver(); 46 | break; 47 | case "Firefox": 48 | FirefoxProfile ff_profile = new FirefoxProfile(); 49 | ff_profile.setPreference("geo.prompt.testing", true); 50 | ff_profile.setPreference("geo.prompt.testing.allow", true); 51 | System.setProperty("webdriver.gecko.driver", seleniumFolderPath + "geckodriver"); 52 | capabilities.setBrowserName("firefox"); 53 | capabilities.setCapability(FirefoxDriver.PROFILE, ff_profile); 54 | 55 | // _driver = new FirefoxDriver(capabilities); 56 | break; 57 | case "InternetExplorer": 58 | System.setProperty("webdriver.ie.driver", seleniumFolderPath + "IEDriverServer.exe"); 59 | capabilities.setBrowserName("internet explorer"); 60 | break; 61 | case "Edge": 62 | System.setProperty("webdriver.edge.driver", seleniumFolderPath + "MicrosoftWebDriver.exe"); 63 | capabilities.setBrowserName("edge"); 64 | break; 65 | case "Safari_iOS": 66 | capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "iPhone 5"); 67 | capabilities.setCapability(MobileCapabilityType.APPIUM_VERSION, "1.6.5"); 68 | capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "iOS"); 69 | capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "XCUITest"); 70 | capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, "Safari"); 71 | break; 72 | default: 73 | System.out.println("Invalid browser passed in: " + browser); 74 | break; 75 | } 76 | 77 | try { 78 | _driver = new RemoteWebDriver(new URL("http://0.0.0.0:4444/wd/hub"), capabilities); 79 | } catch (MalformedURLException e) { 80 | e.printStackTrace(); 81 | } 82 | _driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS); 83 | } 84 | 85 | public void navigateTo(String url) { 86 | _driver.get(url); 87 | } 88 | 89 | public void navigateToBaseUrl() { 90 | _driver.get(getBaseUrl()); 91 | } 92 | 93 | public String getBrowser() { 94 | return this.browserName; 95 | } 96 | 97 | private void setBrowser(String browserName) { 98 | this.browserName = browserName; 99 | } 100 | 101 | private void setBaseUrl(String baseUrl) { 102 | this.baseUrl = baseUrl; 103 | } 104 | 105 | public String getBaseUrl() { 106 | return this.baseUrl; 107 | } 108 | 109 | public HomePage HomePage() { 110 | if (homePage == null) { 111 | homePage = new HomePage(this); 112 | } 113 | return homePage; 114 | } 115 | 116 | // Public properties 117 | public WebDriver _driver; 118 | 119 | // Private properties 120 | private DesiredCapabilities capabilities; 121 | private String browserName; 122 | private String baseUrl; 123 | private String seleniumFolderPath; 124 | private HomePage homePage; 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/pages/HomePage.java: -------------------------------------------------------------------------------- 1 | package pages; 2 | 3 | import helper.Browser; 4 | import pages.shared.Page; 5 | 6 | /** 7 | * Created by opantsjoha on 02/07/2017. 8 | */ 9 | public class HomePage extends Page { 10 | 11 | public HomePage(Browser browser) { 12 | super(browser); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/pages/SearchResultPage.java: -------------------------------------------------------------------------------- 1 | package pages; 2 | 3 | import helper.Browser; 4 | import org.openqa.selenium.WebElement; 5 | import org.openqa.selenium.support.FindBy; 6 | import pages.shared.Page; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Created by opantsjoha on 02/07/2017. 12 | */ 13 | public class SearchResultPage extends Page { 14 | public SearchResultPage(Browser browser) { 15 | super(browser); 16 | } 17 | 18 | // List of all search results 19 | @FindBy(id = "search-content") 20 | private List searchResultList; 21 | 22 | public int getSearchResultSize() { 23 | return searchResultList.size(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/pages/shared/Element.java: -------------------------------------------------------------------------------- 1 | package pages.shared; 2 | 3 | import helper.Browser; 4 | import org.openqa.selenium.JavascriptExecutor; 5 | import org.openqa.selenium.WebDriver; 6 | import org.openqa.selenium.WebElement; 7 | import org.openqa.selenium.support.PageFactory; 8 | import org.openqa.selenium.support.ui.WebDriverWait; 9 | 10 | /** 11 | * Created by opantsjoha on 02/07/2017. 12 | */ 13 | public class Element { 14 | 15 | public Element(Browser browser) { 16 | this.browser = browser; 17 | driver = browser._driver; 18 | PageFactory.initElements(driver, this); 19 | } 20 | 21 | // Draws a red border around the found element. Does not set it back anyhow. 22 | public WebElement highlightElement(WebElement elem) { 23 | // draw a border around the found element 24 | if (driver instanceof JavascriptExecutor) { 25 | ((JavascriptExecutor) driver).executeScript("arguments[0].style.border='3px solid red'", elem); 26 | } 27 | return elem; 28 | } 29 | 30 | protected WebDriver driver; 31 | protected WebDriverWait wait; 32 | protected Browser browser; 33 | protected static int DURATION = 5000; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/pages/shared/HeaderSection.java: -------------------------------------------------------------------------------- 1 | package pages.shared; 2 | 3 | import helper.Browser; 4 | import org.openqa.selenium.WebElement; 5 | import org.openqa.selenium.support.FindBy; 6 | 7 | /** 8 | * Created by opantsjoha on 02/07/2017. 9 | */ 10 | public class HeaderSection extends Element { 11 | 12 | public HeaderSection(Browser browser) { 13 | super(browser); 14 | } 15 | 16 | @FindBy(id = "orb-search-q") 17 | private WebElement searchField; 18 | 19 | // This search button is actually useless - as it just opens another search box 20 | @FindBy(id = "orb-search-button") 21 | private WebElement searchButton; 22 | 23 | @FindBy(className = "se-searchbox__submit") 24 | private WebElement searchSubmitButton; 25 | 26 | public void setSearchField(String value) { 27 | searchField.sendKeys(value); 28 | } 29 | 30 | public void clickOnSearchButton() { 31 | // this element only appears when something is input into search field. 32 | searchSubmitButton.click(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/pages/shared/Page.java: -------------------------------------------------------------------------------- 1 | package pages.shared; 2 | 3 | import helper.Browser; 4 | 5 | /** 6 | * Created by opantsjoha on 02/07/2017. 7 | */ 8 | public class Page extends Element { 9 | public Page(Browser browser) { 10 | super(browser); 11 | } 12 | 13 | // Create HeaderSection object when called. 14 | public HeaderSection HeaderSection(){ 15 | if(headerSection == null){ 16 | headerSection = new HeaderSection(browser); 17 | } 18 | return headerSection; 19 | } 20 | 21 | private HeaderSection headerSection; 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/helper/TestHelper.java: -------------------------------------------------------------------------------- 1 | package helper; 2 | 3 | /** 4 | * Created by opantsjoha on 02/07/2017. 5 | */ 6 | public class TestHelper { 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/tests/HomePageTests.java: -------------------------------------------------------------------------------- 1 | package tests; 2 | 3 | import helper.Browser; 4 | import helper.TestHelper; 5 | import org.testng.annotations.*; 6 | 7 | /** 8 | * Created by opantsjoha on 02/07/2017. 9 | */ 10 | public class HomePageTests extends TestHelper { 11 | 12 | Browser browser; 13 | 14 | @Parameters({"browserName", "baseUrl"}) 15 | @BeforeClass(groups = {"web"}) 16 | public void setUp(String browserName, String baseUrl) { 17 | browser = new Browser(browserName, baseUrl); 18 | browser.navigateToBaseUrl(); 19 | } 20 | 21 | @Test(groups = {"web"}) 22 | public void searchTest() throws InterruptedException { 23 | browser.HomePage().HeaderSection().setSearchField("Cooking"); 24 | browser.HomePage().HeaderSection().clickOnSearchButton(); 25 | // todo: add assert 26 | } 27 | 28 | @AfterClass(groups = {"web"}) 29 | public void tearDown() { 30 | browser._driver.quit(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /test-automation-java.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | --------------------------------------------------------------------------------