Trước tiên, hãy xem xét một ví dụ, điển hình của code auto không sử dụng đối tượng trang (POM):
/***
* Tests login feature
*/
public class SignIn{
public void testSignIn() {
// fill login data on sign-in page
driver.findElement(By.name("user_name")).sendKeys("testUser");
driver.findElement(By.name("password")).sendKeys("my supersecret password");
driver.findElement(By.name("sign-in")).click();
// verify h1 tag is "Hello userName" after login
driver.findElement(By.tagName("h1")).isDisplayed();
assertThat(driver.findElement(By.tagName("h1")).getText(), is("Hello userName"));
}
}
Có hai vấn đề với cách tiếp cận này:
![[Selenium Java] Bài 17: Cấu trúc code theo Page Object Model (POM) | Anh Tester](/uploads/lesson/selenium_java/pom/pom_1.png)
![[Selenium Java] Bài 17: Cấu trúc code theo Page Object Model (POM) | Anh Tester](/uploads/lesson/selenium_java/pom/cau_truc_source_code_pom.png)
Áp dụng các kỹ thuật POM, ví dụ trên có thể được viết lại như thế này trong ví dụ sau về đối tượng trang cho trang Đăng nhập.
Trang này chứa phần Khởi tạo Browser và đóng Driver
package com.anhtester.base;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Parameters;
public class BaseSetup {
private WebDriver driver;
static String driverPath = "resources\\drivers\\";
public WebDriver getDriver() {
return driver;
}
//Hàm này để tùy chọn Browser. Cho chạy trước khi gọi class này (BeforeClass)
private void setDriver(String browserType, String appURL) {
switch (browserType) {
case "chrome":
driver = initChromeDriver(appURL);
break;
case "firefox":
driver = initFirefoxDriver(appURL);
break;
default:
System.out.println("Browser: " + browserType + " is invalid, Launching Chrome as browser of choice...");
driver = initChromeDriver(appURL);
}
}
//Khởi tạo cấu hình của các Browser để đưa vào Switch Case
private static WebDriver initChromeDriver(String appURL) {
System.out.println("Launching Chrome browser...");
System.setProperty("webdriver.chrome.driver", driverPath + "chromedriver.exe");
WebDriver driver = new ChromeDriver();
driver.manage().window().maximize();
driver.navigate().to(appURL);
driver.manage().timeouts().pageLoadTimeout(30, TimeUnit.SECONDS);
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
return driver;
}
private static WebDriver initFirefoxDriver(String appURL) {
System.out.println("Launching Firefox browser...");
System.setProperty("webdriver.gecko.driver", driverPath + "geckodriver.exe");
WebDriver driver = new FirefoxDriver();
driver.manage().window().maximize();
driver.navigate().to(appURL);
driver.manage().timeouts().pageLoadTimeout(30, TimeUnit.SECONDS);
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
return driver;
}
// Chạy hàm initializeTestBaseSetup trước hết khi class này được gọi
@Parameters({ "browserType", "appURL" })
@BeforeClass
public void initializeTestBaseSetup(String browserType, String appURL) {
try {
// Khởi tạo driver và browser
setDriver(browserType, appURL);
} catch (Exception e) {
System.out.println("Error..." + e.getStackTrace());
}
}
@AfterClass
public void tearDown() throws Exception {
Thread.sleep(2000);
driver.quit();
}
}
Trang này chứa phần Locators và xử lý trong trang Sign In
package com.anhtester.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
public class SignInPage {
private WebDriver driver;
private By headerPageText = By.xpath("//a[normalize-space()='Forgot Username/Password?']");
private By emailInput = By.id("EmailInputEmail");
private By passwordInput = By.id("PasswordInputPassword");
private By signinBtn = By.id("SignInButton");
private By errorMsgText = By.id("signInError");
private By pinInput = By.id("Pin");
private By submitBtn = By.id("RequestPinForm_SubmitButton");
private By backBtn = By.id("RequestPinForm_Back");
private By resetPintBtn = By.id("RequestPinForm_ResetPin");
// Khởi tạo class khi được gọi và truyền driver vào để các thành phần trong
// class này đọc
public SignInPage(WebDriver driver) {
this.driver = driver;
}
public String getSignInPageTitle() {
String pageTitle = driver.getTitle();
return pageTitle;
}
public boolean verifySignInPageTitle() {
String expectedTitle = "Sign In";
return getSignInPageTitle().equals(expectedTitle);
}
public boolean verifySignInPageText() {
WebElement element = driver.findElement(headerPageText);
String pageText = element.getText();
String expectedPageText = "Forgot Username/Password?";
return pageText.contains(expectedPageText);
}
// Sau khi thực hiện click Submit thì khởi tạo trang DashboardPage
public void signin(String username, String password, String Pin) throws Exception {
enterEmail(username);
enterPassword(password);
clickSignIn();
Thread.sleep(1000);
enterPin(Pin);
clickSubmit();
}
public boolean verifySignIn() {
enterEmail("test");
enterPassword("pass");
clickSignIn();
return getErrorMessage().contains("incorrect");
}
public void enterEmail(String email) {
WebElement emailTxtBox = driver.findElement(emailInput);
if (emailTxtBox.isDisplayed())
emailTxtBox.sendKeys(email);
}
public void enterPassword(String password) {
WebElement passwordTxtBox = driver.findElement(passwordInput);
if (passwordTxtBox.isDisplayed())
passwordTxtBox.sendKeys(password);
}
public void clickSignIn() {
WebElement signin = driver.findElement(signinBtn);
if (signin.isDisplayed()) {
signin.click();
}
}
public void clickSubmit() {
WebElement submit = driver.findElement(submitBtn);
if (submit.isDisplayed()) {
submit.click();
}
}
public void enterPin(String PIN) {
driver.findElement(pinInput).sendKeys(PIN);
}
public void clickBack() {
driver.findElement(backBtn).click();
}
public void clickResetPin() {
driver.findElement(resetPintBtn).click();
}
public String getErrorMessage() {
String strErrorMsg = null;
WebElement errorMsg = driver.findElement(errorMsgText);
if (errorMsg.isDisplayed() && errorMsg.isEnabled())
strErrorMsg = errorMsg.getText();
return strErrorMsg;
}
public void waitForPageLoaded() {
ExpectedCondition<Boolean> expectation = new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
return ((JavascriptExecutor) driver).executeScript("return document.readyState").toString()
.equals("complete");
}
};
try {
Thread.sleep(1000);
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(expectation);
} catch (Throwable error) {
Assert.fail("Timeout waiting for Page Load Request to complete.");
}
}
}
Cái class này hàm khởi tạo Object (constructor) có 1 tham số chính là driver. Nếu không có tham số này sẽ báo lỗi.
public SignInPage(WebDriver driver) {
this.driver = driver;
}
Trang này chứa các Element cùng Locators của chúng và các hàm xử lý trên trang Sign In
Trang này chạy test case xử lý các bước để Sign In và kế thừa lại class BaseSetup để khởi tạo Browser
package com.anhtester.testcases;
import org.openqa.selenium.WebDriver;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.anhtester.base.BaseSetup;
import com.anhtester.pages.SignInPage;
public class SignInTest extends BaseSetup {
private WebDriver driver;
public SignInPage signInPage;
@BeforeClass
public void setUp() {
driver = getDriver();
}
@Test()
public void signIn() throws Exception {
System.out.println(driver);
signInPage = new SignInPage(driver);
Assert.assertTrue(signInPage.verifySignInPageTitle(), "Sign In page title doesn't match");
Assert.assertTrue(signInPage.verifySignInPageText(), "Header page text not matching");
signInPage.signin("thaian@mailinator.com", "Demo@123", "123456");
}
}
Từ class SignInTest, muốn sử dụng function ở class SignInPage, ta cần phải khởi tạo 1 Object của class đó.
signInPage = new SignInPage(driver);
Đoạn code trên các bạn thấy là tạo đối tượng của class SignInPage và gọi các hàm xử lý trong nó ra dùng thôi chứ không có viết Locators hay driver.findElement gì nữa cả.
Như vậy thì khi có vấn đề lỗi không tìm thấy Element hay sai cú pháp xử lý gì đó thì vào trang SignInPage sửa là xong.
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Page Object test example">
<parameter name="browserType" value="chrome" />
<parameter name="appURL"
value="https://anhtester.startkatalyst.com/account/sign-in" />
<test name="sample test">
<classes>
<class name="com.anhtester.testcases.SignInTest">
<methods>
<include name="signIn" />
</methods>
</class>
</classes>
</test>
</suite>
Trang DashboardPage và DashboardTest y change vậy. Các bạn tự viết thử đi rồi có gì buổi sau An dô sửa.
Gợi ý trang DashboardPage:
package com.anhtester.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
public class DashboardPage {
private WebDriver driver;
public String expectedTitle = "Dashboard";
public String expectedPageText = "My Profile";
private By DocumentModule = By.xpath("//p[normalize-space()='Document Management']");
private By MyProfileModule = By.xpath("//p[normalize-space()='My Profile']");
private By TrainingModule = By.xpath("//p[normalize-space()='Training Management']");
private By headerPageText = By.xpath("//p[normalize-space()='My Profile']");
public DashboardPage(WebDriver driver) {
this.driver = driver;
}
public void openDocumentManagement()
{
driver.findElement(DocumentModule).click();
//Đợi cho đến khi trang load xong với hàm bên dưới
waitForPageLoaded();
}
//Hàm đợi trang load xong rồi thao tác
public void waitForPageLoaded() {
ExpectedCondition<Boolean> expectation = new
ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
return ((JavascriptExecutor) driver).executeScript("return document.readyState").toString().equals("complete");
}
};
try {
Thread.sleep(1000);
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(expectation);
} catch (Throwable error) {
Assert.fail("Timeout waiting for Page Load Request to complete.");
}
}
}
Các bạn viết thêm các hàm xử lý trong trang Dashboard nhen.
Tiếp theo thì viết tiếp DashboardTest class tương tự SignInTest để gọi DashboardPage dùng các hàm đã viết xử lý sẵn dùng thôi.
package com.anhtester.testcases;
import org.openqa.selenium.WebDriver;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.anhtester.base.BaseSetup;
import com.anhtester.pages.DashboardPage;
import com.anhtester.pages.SignInPage;
public class DashboardTest extends BaseSetup {
private WebDriver driver;
public SignInPage signInPage;
public DashboardPage dashboardPage;
@BeforeClass
public void setUp() {
// Đã khởi tạo browser hết rồi kể cả wait, phóng to màn hình,...
driver = getDriver();
}
//SignIn mặc định
@Test(priority = 1)
public void signIn() throws Exception {
System.out.println(driver);
signInPage = new SignInPage(driver);
Assert.assertTrue(signInPage.verifySignInPageTitle(), "Sign In page title doesn't match");
Assert.assertTrue(signInPage.verifySignInPageText(), "Header page text not matching");
signInPage.signin("thaian@mailinator.com", "Demo@123", "123456");
}
//Phần xử lý trang Dashboard
@Test(priority = 2)
public void openDocument() throws Exception {
dashboardPage = new DashboardPage(driver);
Assert.assertTrue(dashboardPage.verifySignInPageTitle(), "Dashboard page title doesn't match");
dashboardPage.openDocumentManagement();
}
}
Rồi viết thêm cái hàm cần chạy bỏ dô file XML bên trên hoặc thêm file XML mới.
==> Tạm thời kết thức phần POM cơ bản tại đây. Tiếp theo sẽ là Page Factory.
Severity: Notice
Message: Undefined variable: new
Filename: post/post_detail.php
Line Number: 384
Backtrace:
File: /home/anhtest2/public_html/application/views/frontend/post/post_detail.php
Line: 384
Function: _error_handler
File: /home/anhtest2/public_html/application/views/frontend/layout/layout_view.php
Line: 591
Function: view
File: /home/anhtest2/public_html/application/core/MY_Controller.php
Line: 34
Function: view
File: /home/anhtest2/public_html/application/controllers/frontend/Post.php
Line: 59
Function: render
File: /home/anhtest2/public_html/index.php
Line: 315
Function: require_once
Severity: Notice
Message: Trying to get property 'slug' of non-object
Filename: post/post_detail.php
Line Number: 384
Backtrace:
File: /home/anhtest2/public_html/application/views/frontend/post/post_detail.php
Line: 384
Function: _error_handler
File: /home/anhtest2/public_html/application/views/frontend/layout/layout_view.php
Line: 591
Function: view
File: /home/anhtest2/public_html/application/core/MY_Controller.php
Line: 34
Function: view
File: /home/anhtest2/public_html/application/controllers/frontend/Post.php
Line: 59
Function: render
File: /home/anhtest2/public_html/index.php
Line: 315
Function: require_once
Anh Tester
Đường dẫu khó chân vẫn cần bước đi
Đời dẫu khổ tâm vẫn cần nghĩ thấu