[Bài 22] Webdriver script không đơn giản thứ nhất

I. Script thực hiện search và check kết quả

Tiêu đề có vẻ ghê chết thôi chứ script của bài này cũng không phải là khó lắm, cứ bình tĩnh. Những bài trước đã nói về các script đơn giản vì các step không quá dài, cách thực hiện step không khó và việc check kết quả cũng không phức tạp. Bài này các bạn sẽ phải làm 1 test mà cách check kết quả của nó phức tạp hơn nhiều lần, còn các step thì vẫn đơn giản.

  • Test chức năng search ở Page AllPosts

Lưu ý: Tìm kiếm của WordPress sẽ trả về kết quả là 1 list các bài viết mà có Title hoặc Body có chứa từ khóa đó. Ví dụ như trong hình, có đến 3 bài viết ở dưới là không có từ khóa trong Title.

Khi search thì kết quả trả về sẽ có 2 trường hợp:

  • Không có kết quả nào match (các bạn tự xử lý)
  • Có kết quả match

Nếu sau khi search, có nhiều kết quả trả về, ta lại gặp vào 2 trường hợp:

  • Số lượng post có chứa keyword ít, nhỏ hơn hoặc bằng 1 page kết quả.
  • Số lượng post có chứa keyword lớn, có nhiều hơn 1 page kết quả (sẽ viết 1 bài về phân trang sau).

Chốt lại testcase của ta sẽ xử lý là:

  • Test chức năng search, khi có số lượng bài viết match với keyword chỉ nằm trên 1 page. Nói cách khác, số lượng bài viết lớn hơn 1 và nhỏ hơn hoặc bằng 20 vì page phân trang mặc định của wordpress là 20 items.

Với testcase này, ta sẽ có flow test như sau:

  1. Vào trang AllPosts
  2. Điền keyword và search
  3. Kiểm tra title và body của từng bài viết xem có chứa keyword hay không.

Nhưng sẽ không giống như những bài trước, ở bài này mình sẽ hướng dẫn cách viết Test theo phương pháp Test-Driven, có nghĩa là ta sẽ viết Test case trước khi viết các phần Implement code.

  1. Viết luôn test, mình muốn trình tự test và cách check kết quả thế nào thì phải xác định trước.
  2. Step nào chưa làm thì tự implement từng cái 1, theo thứ tự trừ trên xuống dưới.

Test case sẽ như sau:

@Test
public void searchPost(){
	loginPg = new LoginPage(driver);
    dashBoardPg = loginPg.loginSuccess();
    allPostPg = dashBoardPg.moveToAllPostsPage();
    allPostsPg.searchPost("ocean");
    Assert.assertTrue(allPostPg.hasKeyword("ocean"));
}

Với method searchPost, ta chỉ cần implement đơn giản như sau:

public class AllPostsPage extends BasePage {
	
	...
	@FindBy(id = "post-search-input")
	private WebElement searchInput;

	@FindBy(id = "search-submit")
	private WebElement searchBtn;
	....

	public void searchPost(String keyword) {
		searchInput.sendKeys(keyword);
		searchBtn.click();
	}
}

Với method hasKeyword, ta kỳ vọng là nó sẽ return lại kết quả là True, có nghĩa là Title hoặc Body sẽ có chứa keyword đó:

  1. Trong list các Post sau khi search, check lần lượt xem các Title có chứa keyword không.
  2. Nếu gặp Title không chứa keyword thì phải open post, rồi check body xem có keyword không.
  3. Sau khi so sánh trong body xong rồi thì back lại về trang list kết quả. Nếu cả Title và Body đều không có keyword thì return False.
  4. Tiếp tục làm tương tự với post tiếp theo. Nếu tất cả các post có chứa keyword thì return True.

Mình sẽ viết đoạn này như sau:

public class AllPostsPage extends BasePage {
	...
	@FindBy(css = "#the-list strong a")
	private List<WebElement> postList;
	...

    public boolean hasKeyword(String keyword) {
        String kw = keyword.toLowerCase();
        List<Boolean> searchValues = new ArrayList<>();
        for (int i = 0; i < postList.size(); i++) {
            boolean found = false;
            if(titleHasKeyword(i, kw)){
                found = true;
            } else {
                found = bodyHasKeyword(kw);
            }
            searchValues.add(found);
        }
        
        return searchValues.stream().allMatch(v -> v);
    }

    private boolean titleHasKeyword(int index, String kw) {
        String postTitle = postList.get(index).getText().toLowerCase();
        return postTitle.contains(kw);
    }

    private boolean bodyHasKeyword(String kw) {
        List<WebElement> paragraphs = driver.findElements(By.cssSelector("#tinymce p"));
        return paragraphs.stream()
                .map(p -> p.getText().toLowerCase())
                .anyMatch(content -> content.contains(kw));
    }
}

Phân tích từng đoạn code:

Đây là khai báo PageFactory cho List các Elements, ở đây là list các Posts.

@FindBy(css = "#the-list strong a")
private List<WebElement> postList;

Đây là đoạn check xem title có chứa keywork không.

private boolean titleHasKeyword(int index, String kw) {
    String postTitle = postList.get(index).getText().toLowerCase();
    return postTitle.contains(kw);
}

Check từng paragraph xem có keyword không, nếu chỉ cần 1 paragraph có keyword, lập tức return true. Mình dùng function anyMacth() của Java Stream

private boolean bodyHasKeyword(String kw) {
    List<WebElement> paragraphs = driver.findElements(By.cssSelector("#tinymce p"));
    return paragraphs.stream()
            .map(p -> p.getText().toLowerCase())
            .anyMatch(content -> content.contains(kw));
}

Vì sao lại check paragraphs? Đó là vì 1 body là tập hợp của nhiều paragraph khác nhau, và rất nhiều các thẻ loại khác như h1,h2,..,h6, pre nhưng mà mình lười nên mình chỉ viết cái method check paragraphs thôi, các loại khác cũng làm tương tự.

Cuối cùng check cả post có keyword hay không.

public boolean hasKeyword(String keyword) {
    String kw = keyword.toLowerCase();
    List<Boolean> searchValues = new ArrayList<>();
    for (int i = 0; i < postList.size(); i++) {
        boolean found = false;
        if(titleHasKeyword(i, kw)){
            found = true;
        } else {
            found = bodyHasKeyword(kw);
        }
        searchValues.add(found);
    }

    return searchValues.stream().allMatch(v -> v);
}

Logic của đoạn này như sau:

Vì mình có nhiều post phải check, nên mình sẽ gom tất cả kết quả check của các post vào trong 1 List List searchValues

  • Với mỗi post –> check title trước –> nếu title có kw –> true và dừng luôn.
  • Nếu title ko có kw –> check tiếp body –> body có kw –> true, nếu ko có return false.
  • Lưu kết quả này vào trong searchValues

Cuối cùng gom tất cả kết quả này bằng function allMatch() của Java Stream. Nếu toàn bộ các post đều chứa keyword thì return true, nếu chỉ 1 post không thỏa mãn thì return false.

II. Kết bài

Vậy thôi, bài cũng hơi dài là lắm thứ loằng ngoằng, mình không thể giải thích được hết được, hi vọng là sau nhiều bài với độ phức tạp tăng dần, các bạn sẽ hiểu hơn về cách viết code Selenium Webdriver. Nó không khó cũng chẳng dễ, nó chỉ cần kỹ năng lập trình cơ bản và động lực để học thôi. 😀

#Edit 1: Sửa lại toàn bộ code, update sử dụng Java Stream function.

5 1 vote
Article Rating
Subscribe
Notify of
guest
19 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
trackback

[…] [Bài 22] Webdriver script không đơn giản thứ nhất […]

Nguyen An
Nguyen An
6 years ago

Cảm ơn những bài viết vừa đáng yêu vừa bổ ích của anh.
Chờ đợi những bài kế tiếp ạ 🙂

Hiep
Hiep
6 years ago

Series của bạn đã giúp đỡ mình vượt qua được những bước đầu tiên với automation. Hi vọng là bạn có thể dành chút thời gian và cho ra nhiều bài hơn nữa.

van anh
van anh
5 years ago

em cũng đang hóng những bài viết của anh

Tiêu Tiêu
Tiêu Tiêu
5 years ago

anh ơi cho em hỏi “postList” gọi từ đâu vậy ạ

Tiêu Tiêu
Tiêu Tiêu
5 years ago

Nhờ anh giải thích hộ em đoạn này với ạ:
driver.switchTo().frame(“content_ifr”);

driver.switchTo().defaultContent();

Lv
Lv
5 years ago

trong hasKeyword có dòng driver.navigate().back();
bạn có thể giải thích vì sao dùng dòng ở đây ko ?
thanks.

toàn
toàn
3 years ago

với cốc cốc thì khởi tạo driver như thế nào a

Trà
Trà
2 years ago

Cảm ơn những bài viết của em . Nó giúp em rất nhiều.
Em muốn hỏi :

Vì sao lại check paragraphs? Đó là vì 1 body là tập hợp của nhiều paragraph khác nhau, và rất nhiều các thẻ loại khác như h1,h2,..,h6, pre nhưng mà mình lười nên mình chỉ viết cái method check paragraphs thôi, các loại khác cũng làm tương tự.

Trong đoạn này , mình chỉ cần check xem body của trang post đó có keyword mình tìm kiếm hay không thôi(là chỉ check content thôi đúng ko ạ), thì tại sao mình lại phải check thêm các thẻ như h1 h2 làm gì ạ?

Khoa Dinh
Khoa Dinh
3 months ago

Dòng 13 của phương thức hasKeyword, mình nghĩ lẽ ra nên check if bodyHasKeyword(keyword) == false thì return false. Để như hiện tại thì hàm check bodyHasKeyword đâu có giá trị gì