NỘI DUNG BÀI HỌC
Giới thiệu Page Factory
Page Factory là phần mở rộng của Page Object Model, nó giúp khởi tạo các đối tượng WebElement tối ưu hơn và giảm thiểu code ngắn gọn hơn.
Nó được sử dụng để khởi tạo các đối tượng Trang hoặc để khởi tạo chính đối tượng Trang. Nó cũng được sử dụng để khởi tạo các hành động xử lý mà không cần sử dụng cú pháp driver.findElement / driver.findElements.
Có hai bước đơn giản để sử dụng Page Factory trong các dự án Selenium:
1. Sử dụng chú thích @FindBy - Không giống như cách tiếp cận thông thường để khởi tạo các phần tử trang web bằng FindElement hoặc FindElements , Page Factory sử dụng chú thích @FindBy. Các chú thích này được sử dụng trong Page Factory mang tính mô tả. Hơn nữa, chúng giúp cải thiện khả năng đọc mã mà chúng ta sẽ thảo luận trong phần tiếp theo. Nó cung cấp cú pháp sau để xác định vị trí các phần tử web:
@FindBy(id="userName")
WebElement username;
@FindBy có thể chấp nhận tagName, partialLinkText, name, linkText, id, css, className, xpath dưới dạng thuộc tính.
2 . Khởi tạo các phần tử bằng initElements () - Đây là một phương thức tĩnh được sử dụng để khởi tạo các phần tử web mà chúng định vị bằng cách sử dụng @FindBy trên hoặc (các) chú thích khác, đặt nó vào chổ hàm xây dựng khởi tạo lớp trang.
PageFactory.initElements(WebDriver driver, java.lang.Class.pageObjectClass);
Sử dụng Page Factory trong POM
Chúng ta thấy class SignInPage bài trước khi chưa dùng Page Factory:
package anhtester.com.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
public class SignInPage {
//Chứa cái object (element), biến, hàm xử lý cho test case,...
WebDriver driver = null;
String pageTitle = "Project Manager | Anh Tester Demo";
//Khai object element của trang CRM
By emailInput = By.xpath("//input[@id='email']");
By passwordInput = By.xpath("//input[@id='password']");
By signinBtn = By.xpath("//button[@type='submit']");
By forgotPasswordLink = By.xpath("//a[normalize-space()='Forgot password?']");
//Onject forgot password
By messageNoEmail = By.xpath("//div[@class='app-alert-message']");
//Object for Sign Up
//5 object nữa
//Hàm xây dựng
public SignInPage(WebDriver _driver) {
driver = _driver; //truyền giá trị driver khi được khởi tạo ở đâu đó vào chính class này để dùng.
}
//Viết hàm xử lý sign in
public void signIn(String email, String password) {
driver.get("https://crm.anhtester.com/signin");
verifyPageTitle();
enterEmail(email); //Chứa wait, chưa verify tồn tại, điền giá trị
enterPassword(password);
clickOnSignInBtn();
}
public void forgotPassword(String email) {
driver.get("https://crm.anhtester.com/signin");
clickForgotPasswordLink(); //Click link forgot
enterEmail(email);
clickOnSignInBtn();
//Bắt sự kiện Message hiển thị sau khi submit
verifyMessageEmail("Sorry, no account found with this email."); //Verify No Email
///verifyMessageEmail("Success ! Please check your email to reset password."); //Verify exist Email and reset password
}
public void signUp() {
//Bài tập về nhà
}
public void enterEmail(String email) {
WebDriverWait wait = new WebDriverWait(driver, 5);
wait.until(ExpectedConditions.visibilityOfElementLocated(emailInput));
WebElement emailElement = driver.findElement(emailInput);
Assert.assertTrue(emailElement.isDisplayed(), "Không hiển thị element Email");
emailElement.sendKeys(email);
}
public void enterPassword(String password) {
WebDriverWait wait = new WebDriverWait(driver, 5);
wait.until(ExpectedConditions.visibilityOfElementLocated(passwordInput));
WebElement emailElement = driver.findElement(passwordInput);
emailElement.sendKeys(password);
}
public void clickOnSignInBtn() {
WebDriverWait wait = new WebDriverWait(driver, 5);
wait.until(ExpectedConditions.visibilityOfElementLocated(signinBtn));
WebElement emailElement = driver.findElement(signinBtn);
emailElement.click();
}
public void clickForgotPasswordLink() {
driver.findElement(forgotPasswordLink).click();
}
public void verifyPageTitle() {
Assert.assertEquals(driver.getTitle(), pageTitle);
}
public void verifyPageUrl() {
Assert.assertEquals(driver.getCurrentUrl(), "https://crm.anhtester.com/dashboard", "Chuyển hướng chưa đúng Dashboard page");
}
public void verifyMessageEmail(String message) {
Assert.assertEquals(driver.findElement(messageNoEmail).getText(), message);
}
}
Khi chúng ta áp dụng dùng Page Factory thì nó sẽ như này:
public class SignInPageFactory {
private WebDriver driver;
@FindBy(id = "email")
private WebElement emailInput;
@FindBy(id = "password")
private WebElement passwordInput;
@FindBy(xpath = "//button[@type='submit']")
private WebElement signinBtn;
// Khởi tạo class khi được gọi và truyền driver vào để các thành phần trong
// Và khởi tạo initElements
public SignInPageFactory(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
// Chúng ta viết hàm signin không cần dùng các hàm bổ trợ enter hay click nữa
public void signin(String username, String password, String Pin) throws Exception {
emailInput.sendKeys(username);
passwordInput.sendKeys(password);
signinBtn.click();
Thread.sleep(1000);
}
}
Vậy là xong. Gọn ràng hơn chứ hả.
Ta thấy cách khai báo một WebElement khá rõ ràng
@FindBy(id = "email")
private WebElement emailInput;
Nếu dùng xpath hay name hay classname thì cứ thay vào chổ "id" là xong.
Hàm khởi tạo (constructor) sẽ thay đổi thêm 1 dòng khởi tạo initElements vì bây giờ sẽ sử dụng thêm PageFactory.
public SignInPageFactory(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
Diễn giải thêm chổ Page Factory:
Vậy thì chính xác là Page Factory làm gì?
Sau khi khai báo WebElement và Locator, thì 2 cái này phải được liên kết với nhau. Có nghĩa là WebElement sẽ luôn luôn được tìm thấy sử dụng bởi Locator thông qua annotation @FindBy. Và thực hiện công việc trên, ta cần khởi tạo Element thông qua constructor:
PageFactory.initElements(driver, this);
- Các WebElement sẽ KHÔNG được tìm thấy ngay khi khởi tạo. Khởi tạo chỉ để tạo ra LIÊN KẾT giữa WebElement và Locator. Khi nào WebElement đó được sử dụng thì chúng mới được tìm kiếm dựa trên các biến loại WebElement đã được khai báo ở trên.
- Nếu WebElement đó được sử dụng nhiều lần thì nó tiếp tục tìm kiếm nhiều lần hay sao đây??
Chính xác là như vậy, nó sẽ được tìm kiếm mỗi lần sử dụng. Tuy nhiên, có 1 cách để chỉ tìm 1 lần rồi sử dụng lại, đó là sử dụng annotation @CacheLookup. Ví dụ:
@FindBy(id = "email")
@CacheLookup
private WebElement emailInput;
Ngoài ra, bạn có thể sử dụng @FindAll với nhiều chú thích @FindBy để tìm kiếm các phần tử khớp với bất kỳ bộ định vị nào đã cho (điệu kiện HOẶC):
@FindAll({@FindBy(how=How.ID, using="username"),
@FindBy(className="username-field")})
private WebElement user_name;
Ví dụ:
@FindAll({
@FindBy(id="btn", //doesn't match
@FindBy(name="sbmtBtn"), //Matches
@FindBy(class="btn-primary") //doesn't match
})
WebElement submitButton;
Chú thích ở trên sẽ định vị được element submitButton mặc dù chỉ có một tiêu chí phù hợp với @FindAll hoạt động trên một hoặc nhiều tiêu chí. (theo điều kiện ở giữa)
Hoặc khi cần tìm danh sách các phần tử trên một trang (findElements) thì chúng ta dùng @FindBys như sau:
@FindBys(@FindBy(css="div[class='yt-lockup-tile yt-lockup-video']")))
private List<WebElement> videoElements;
Và cách khác để viết đơn giản hơn nữa là:
@FindBy(how=How.CSS,using="div[class='yt-lockup-tile yt-lockup-video']")
private List<WebElement> videoElements;
@CacheLookUp
Chú thích @CacheLookUp rất hữu ích khi bạn đề cập đến cùng một phần tử web nhiều lần. Hãy xem xét một ứng dụng mà mỗi trường hợp thử nghiệm yêu cầu hoạt động Đăng nhập. Trong trường hợp như vậy, sử dụng @CacheLookUp, chúng ta có thể lưu trữ các phần tử web trong bộ nhớ đệm ngay sau khi đọc lần đầu tiên. Nó kết nối việc thực thi của mình và mã code khi tìm kiếm element, không cần phải tìm kiếm phần tử trên trang web và tham chiếu trực tiếp nó từ bộ nhớ nữa. Nó nhanh hơn.
@CacheLookUp có thể được đặt trước bằng bất kỳ chú thích nào được thảo luận ở trên, tức là @FindBy, @FindBys & @FindAll.
Ví dụ:
@CacheLookUp
@FindBys({
@FindBy(class="custom-control-check-box"),
@FindBy(id="game-chk-box")
})
WebElement chkBox;
Lưu ý: Chúng ta nên sử dụng @CacheLookUp với các phần tử web có giá trị thuộc tính ít khi thay đổi. Vì nó duy trì một tham chiếu trong bộ nhớ nếu giá trị định vị thay đổi thì code của bạn sẽ không định vị phần tử đúng nữa, vì nó sẽ luôn tham chiếu đến bộ nhớ đệm.
==> Vậy là xong rồi.
Phần DashboardPage viết tương tự thôi nào =))