Nội dung bài viết
I. Sử dụng Page Factory
Xem hướng dẫn ở nhiều page, chắc các bạn cũng quen thuộc với Page Factory rồi, nay mình sẽ sử dụng nó để tối ưu code hiện tại. 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 và giảm thiểu code dài dòng.
Login trước khi có sử dụng Page Factory:
public class LoginPage { public String user_login = "user_login"; public String user_pass = "user_pass"; public String submitBtn = "wp-submit"; WebDriver driver; public LoginPage(WebDriver driver) { this.driver = driver; } public void login(String userName, String password) { driver.findElement(By.id(user_login)).sendKeys(userName); driver.findElement(By.id(user_pass)).sendKeys(password); driver.findElement(By.id(submitBtn)).click(); } }
Login Page sau khi có sử dụng Page Factory:
public class LoginPage { @FindBy (id = "user_login") WebElement user_login; @FindBy (id = "user_pass") WebElement user_pass; @FindBy (id = "wp-submit") WebElement submitBtn; WebDriver driver; public LoginPage(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); } public void login(String userName, String password) { user_login.sendKeys(userName); user_pass.sendKeys(password); submitBtn.click(); } }
Ta thấy cách khai báo một WebElement khá rõ ràng
@FindBy (id = "user_login") WebElement user_login;
Nếu Element đó được xác định bằng name, css, xpath… thì bạn thay tương ứng vào chỗ “id” là được.
Hàm khởi tạo (constructor) sẽ thay đổi vì bây giờ sẽ sử dụng thêm PageFactory.
public LoginPage(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); }
Tương tự, ta sẽ xử lý nốt cái page AddNewPostPage:
public class AddNewPostPage { public String URL_addNew = "http://localhost/wp/wp-admin/post-new.php"; @FindBy(id = "content_ifr") WebElement iframe; @FindBy(id = "title") WebElement titleID; @FindBy(id = "tinymce") WebElement bodyID; @FindBy(id = "publish") WebElement publishBtn; @FindBy(css = "#message a") WebElement linkNewPost; @FindBy(css = "#edit-slug-box > strong") WebElement permalink; WebDriver driver; WebDriverWait wait; public AddNewPostPage(WebDriver driver) { this.driver = driver; wait = new WebDriverWait(driver, 20); PageFactory.initElements(driver, this); } public void addANewPost(String title, String body) { titleID.sendKeys(title); driver.switchTo().frame(iframe); bodyID.sendKeys(body); driver.switchTo().defaultContent(); wait.until(ExpectedConditions.visibilityOf(permalink)); publishBtn.click(); } public void goToDetailPage() { linkNewPost.click(); } }
LƯU Ý: sau khi các bạn copy code của mình, sẽ bị báo lỗi rất nhiều do sử dụng các method của các package khác. Các bạn hãy sử dụng Ctrl+Shift+O để Eclipse tự động import thư viện cần thiết.
Bạn có thể dùng live template của Intellij để viết cho nhanh hơn.
II. Page Factory In-depth
Q: Hey man, vậy thì chính xác là Page Factory làm gì?
A: 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 bởi sử dụng 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);
Q: Ơ, có nghĩa là các WebElement sẽ được tìm thấy ngay khi khởi tạo à?
A: Không. Khi khởi tạo, các WebElement sẽ chưa được tìm thấy. 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.
Q: Ơ, 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 à?
A: Câu hỏi hay. 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 = "user_login") @CacheLookup WebElement user_login;
Q: Một câu hỏi ngoài luồng: bao giờ thì series này kết thúc thế?
A: Mình chịu, biết đến đâu, viết đến đó. 😀
[…] ← Previous Next → […]
Anh cho em hỏi tại sao mình khởi tạo WebDriver driver; rồi. Nhưng vẫn phải thêm this.driver = driver nữa ạ? Mục đích của phần this.driver = driver này để làm gì ạ? Vì toàn bộ những lệnh bên dưới mình đều dùng driver chứ không phải this.driver.
Em cảm ơn anh!
Mình đã khởi tạo ở chỗ nào em?
1. WebDriver driver; –> khai báo 1 biến tên là driver, có kiểu dữ liệu là WebDriver, hoàn toàn ko trỏ đến bất kỳ 1 Object cụ thể nào cả. —> driver này đang là null
2. public LoginPage(WebDriver driver) {
this.driver = driver;
}
—> gán driver ở bước 1 vào Webdriver Object (đã tồn tại), được truyền vào từ argument. —> đến đây thì driver mới hết null và trỏ vào 1 Object cụ thể.
Oh, do em chưa hiểu rõ ạ
Em cảm ơn a đã giải thích :))
Anh Giang cho em hỏi chút là với mấy dynamic element thì bình thường e dùng xpath và [contains()], [starts-with()] để tìm.
Vậy với @FindBy mình có thể dùng contains, starts-with để find dynamic element ko a?
Thanks a
Không vấn đề gì cả, cứ dùng thôi.
Ví dụ:
@FindBy (xpath = "//*[contains(text(), 'Author')]")
em hỏi thêm 1 chút là ngoài giảm code dài dòng, nó còn có thêm benefit nào lớn nữa ko anh?
framework em đang làm ko dùng page factory nên code hơi dài, em tính đề xuất dùng page factory để refactor lại nhưng ko biết còn benefit nào lớn ko? để thuyết phục PO về priority.
Thanks anh
Không nhất thiết phải dùng Page Factory đâu em, đôi khi chính nó lại là rắc rối đấy, nó rất dễ bị stale element exception.
Để refactor code, em cứ follow theo SOLID và các practices của refactors thôi.
Cu de im vay chu refactor lam gi. Project moi thi duoc