Nội dung bài học

Cách dùng các loại Wait:  Implicit Wait, Explicit WaitFluent Wait trong Selenium Webdriver Java

Selenium Webdriver như đã giới thiệu ở bài số 3 rồi thì nó là thư viện để tương tác với browser, nhưng khi chạy code có thể với nhiều nguyên nhân mà mỗi lần chạy lại khác nhau, trang web có thể load lâu hơn, nhanh hơn, khiến cho test script của bạn liên tục gặp phải Exception.

Để khắc phục điều này thì Selenium Webdriver cung cấp 3 loại Wait để giúp cho test script trở nên ổn định hơn, đồng bộ hóa giữa những lần run test.

[Selenium Java] Bài 15: Cách dùng Wait trong Selenium Java | Anh Tester

Cái chúng ta sử dụng nhiều hổm rài là Thread.sleep(timeout) thì các bạn cũng biết nó ý nghĩa gì rồi đó. Nó dùng để chờ đợi chính xác cụ thể thời gian cho từng dòng lệnh thực thi.

Thread.sleep thuộc về Java và sẽ khó chờ đợi ngay cả sau khi phần tử được hiển thị. Ít ai dùng thằng này khi xây dựng thành hình Framework. Vì nó không chính xác cho chung cả hệ thống.

Và giờ chúng ta tìm hiểu phần chính là Wait trong Selenium

I. Implicit Wait

Dịch ra tiếng việt là “đợi ngầm”, có nghĩa là nó sẽ luôn tìm kiếm Element trong 1 khoảng thời gian ngầm định trước khi văng ra No Such Element Exception.

Cú pháp:

driver.manage().timeouts().implicitlyWait(TimeOut, TimeUnit.SECONDS);


Ví dụ:

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

 

Ví dụ trên mình sẽ luôn tìm kiếm 1 element trong khoảng thời gian timeout là 10s. Sau 10s mà không tìm thấy thì sẽ văng ra Exception.

Code nào:

package anhtester.Wait;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.testng.annotations.Test;

import Initiation.Init;

public class ImplicitWaitClass extends Init {

	@Test
	public void ImplicitWaitDemo() {
		
		driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
		
		driver.get("https://anhtester.com/");
		driver.findElement(By.xpath("//h3[contains(text(),'Website Testing')]")).click();	
	}
}


Lưu ý:

Phương thức Wait này nó là static setting, có nghĩa là nó sẽ áp dụng cho tất cả các trường hợp có sử dụng method findElement(). Điều này dẫn đến 2 kết quả:

  • Bạn chỉ cần viết 1 dòng này duy nhất ở trong project, không cần viết đến dòng thứ 2. Thi thoảng mình vẫn thấy các bạn fresher mới học viết dòng này tràn lan ở bất kỳ chỗ nào mà bạn cho là phải đợi =))
  • Giả sử bạn viết dòng này ở trong test1() thì nó vẫn sẽ có tác dụng ở test2()test3()

Và hậu quả của việc này là:

  • Như đã nói ở trên, thời gian run test vì nhiều nguyên nhân nên chẳng có lần nào giống lần nào. Khi page load nhanh, bạn để timeout là 5s, khi page load chậm, bạn bị lỗi, phải tăng lên 10s. Sau 1 hồi sửa đi sửa lại, có thể bạn sẽ setup timeout là 30s 40s gì đó và đặt nó ở trong @BeforeTest để nó kéo dài được lâu hơn ahaha. Và thế là bạn tự làm chậm test auto của mình luôn rồi =))
  • Có những khi Web thay đổi UI, thay vì test fail ngay lập tức thì nó sẽ phải đợi 1 khoảng thời gian để báo fail. Nếu 1, 2 test thì cũng không vấn đề nhưng nếu có 100 tests thì thời gian chờ lãng phí rất là nhiều.


Vì vậy để sử dụng hiệu quả thì An khuyên các bạn:

  • Nên sử dụng kết hợp giữa Implicit WaitExplicit Wait được nêu ở phần sau vì để tránh lãng phí thời gian hơn.
  • Nếu có sử dụng Implicit Wait trong 1 test nào đó mà thấy không cần nữa thì có thể clean up ở @AfterTest để reset lại thời gian timeouts, để nó không còn ảnh hưởng đến những test khác. Khi nào cần thì setup lại.
@AfterTest
public void tearDown(){
    driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS);
    driver.quit();
}​

 

II. Explicit Wait

Dich tiếng việt là “cố tình đợi”, Explicit wait được sử dụng để tạm dừng việc thực thi script cho đến khi một điều kiện cụ thể được chỉ định được đáp ứng hoặc thời gian tối đa đã trôi qua.

Khác với Implicit wait, Explicit wait chỉ được áp dụng cho một trường hợp cụ thể thay vì dùng cho toàn bộ các lệnh trong script.

Nó đợi theo trạng thái (state) của Element và Page thay vì phụ thuộc vào thời gian (timeout).

Nếu chúng ta đang sử dụng kết hợp cả hai loại Wait trên thì có 2 trường hợp như sau:

    1. Nếu Explicit Wait dùng câu lệnh tìm kiếm là đối tượng By thì Explicit Wait sẽ được ưu tiên đầu tiên.
    2. Nếu Explicit Wait dùng câu lệnh tìm kiếm là findElement thì Implicit Wait sẽ được ưu tiên đầu tiên.


Explicit Wait nằm trong pagekage org.openqa.selenium.support.ui  với 2 package con:

  • import org.openqa.selenium.support.ui.ExpectedConditions
  • import org.openqa.selenium.support.ui.WebDriverWait


Khởi tạo Object cho class WebDriverWait

WebDriverWait wait = new WebDriverWait(driver,10);

Tạo một biến tham chiếu “wait” cho lớp WebDriverWait và khởi tạo nó bằng cách sử dụng biến thể WebDriver và khai báo thời gian chờ tối đa để quá trình thực thi tạm dừng. Thời gian chờ tối đa này được tính bằng "giây" (trong ví dụ này đang set là 10s)

Ví dụ:

WebDriverWait wait = new WebDriverWait(driver, 10);

wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//h3[contains(text(),'Website Testing')]")));

driver.findElement(By.xpath("//h3[contains(text(),'Website Testing')]")).click();


Expected Condition

Lớp ExpectedConditions cung cấp một bộ trợ giúp tuyệt vời để giải quyết các tình huống trong đó chúng ta phải xác định điều kiện xảy ra trước khi thực thi các step test thực tế.

Lớp ExpectedConditions đi kèm với một loạt các điều kiện mong đợi có thể được truy cập với sự trợ giúp của biến tham chiếu WebDriverWait và phương thức until().


Một số Expected Conditions điển hình

#1) elementToBeClickable() – Điều kiện mong đợi là chờ một phần tử có thể click được, tức là phần tử đó phải hiện diện / được hiển thị trên màn hình và phải enabled (có thể click).

Code:

wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//div[contains(text(),'Anh Tester')]")));

#2) textToBePresentInElement() – Điều kiện mong đợi là chờ một phần tử chứa đoạn text được chỉ định.

Code:

wait.until(ExpectedConditions.textToBePresentInElement(By.xpath("//div[@id= 'forgotPass'"), "text to be found"));

#3) alertIsPresent() – Điều kiện mong đợi là chờ một hộp cảnh báo xuất hiện.

Code:

wait.until(ExpectedConditions.alertIsPresent()) !=null);

#4) titleIs() – Điều kiện mong đợi là chờ một trang có tiêu đề cụ thể.

Code:

wait.until(ExpectedConditions.titleIs("Anh Tester - Automation Testing"));

#5) frameToBeAvailableAndSwitchToIt() – Điều kiện mong đợi là chờ một khung có sẵn (available) và ngay sau khi có khung, điều khiển sẽ tự động chuyển sang nó.

Code:

wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(By.id("newframe")));

#6) visibilityOfElementLocated() - Điều kiện mong đợi là chờ và kiểm tra một element tồn tại trong DOM (trên web)
Code:

wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//h3[contains(text(),'Website Testing')]")));
		
driver.findElement(By.xpath("//h3[contains(text(),'Website Testing')]")).click();

 

Code nào:

package anhtester.Wait;

import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.Test;

import Initiation.Init;

public class ExplicitWaitClass extends Init {

	@Test
	public void ExplicitWaitDemo() {

		driver.get("https://anhtester.com/");

		WebDriverWait wait = new WebDriverWait(driver, 10);

		wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//h3[contains(text(),'Website Testing')]")));

		driver.findElement(By.xpath("//h3[contains(text(),'Website Testing')]")).click();
	}
}


Nó sẽ bảo WebDriver là đợi cho đến khi điều kiện (ExpectedCondition) được thỏa mãn hoặc hết thời gian timeouts, nó sẽ bắn ra exception tùy theo điều kiện, ví dụ như ElementNotVisibleException, ElementNotInteractableException, TimeoutException…

Có 2 điểm lưu ý:

  • Trong thời gian đợi, nếu có exception NotFoundException thì exception cũng sẽ bị bỏ qua.
  • Và WebDriver sẽ check điều kiện có được thỏa mãn không sau 1 khoảng interval là 500ms



Selenium đã viết rất nhiều các condition khác nhau, lựa chọn sử dụng cho phù hợp. Bạn tự tìm hiểu thêm nhé.

[Selenium Java] Bài 15: Cách dùng Wait trong Selenium Java | Anh Tester


Tuy nhiên, vì mục tiêu viết code để dễ hiểu, phù hợp hơn với business domain, hoặc muốn 1 cái điều kiện mà Selenium chưa cung cấp thì ta hoàn toàn có thể tự viết custom condition cho riêng test của mình. Mình sẽ viết 1 bài về custom Expected condition sau.

III. Fluent Wait

Mục tiêu của Fluent Wait là cung cấp 1 cơ chế Wait chung, có thể ứng dụng được nhiều chỗ, không chỉ ứng dụng cho mỗi WebDriver, và nó chính là cha của Explicit Wait phía trên. Nói chính xác hơn thì

public class WebDriverWait extends FluentWait<WebDriver>;
 

Cách dùng:

Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
                .withTimeout(Duration.ofSeconds(30))
                .pollingEvery(Duration.ofSeconds(5))
                .ignoring(NoSuchElementException.class);

// Chờ 30 giây để một phần tử hiện diện trên trang
// Và sẽ thực hiện lặp lại mỗi 5 giây nếu chưa tìm thấy phần tử đó

Sau đó, bạn vẫn sử dụng như Wait của Explicit Wait thôi

wait.until(ExpectedConditions.elementToBeClickable(linkToClick));

 

Code nào:

package anhtester.Wait;

import java.time.Duration;

import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Wait;
import org.testng.annotations.Test;

import Initiation.Init;

public class FluentWaitClass extends Init {

	@Test
	public void FluentWaitDemo() {

		driver.get("https://anhtester.com/");

		Wait<WebDriver> wait = new FluentWait<WebDriver>(driver).withTimeout(Duration.ofSeconds(30))
				.pollingEvery(Duration.ofSeconds(5)).ignoring(NoSuchElementException.class);

		wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//h3[contains(text(),'Website Testing')]")));

		driver.findElement(By.xpath("//h3[contains(text(),'Website Testing')]")).click();
	}
}


Bạn có thể viết dạng này cho 1 đối tượng WebElement:
(Thêm này dô import java.util.function.Function;)

WebElement loginButton = wait.until(new Function<WebDriver, WebElement>() {
			public WebElement apply(WebDriver driver) {
				return driver.findElement(By.id("btn-login"));
			}
		});
		
		loginButton.click();

Cộng đồng Automation Testing Việt Nam:


🌱 Zalo
Automation Testing:   https://zalo.me/g/lsxswc560
🌱 Facebook Group: Cộng đồng Automation Testing Việt Nam (Website, Desktop, Mobile)
🌱 Facebook Fanpage: Cộng đồng Automation Testing Việt Nam - Selenium

  • 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


Cộng đồng Automation Testing Việt Nam:


🌱 Zalo
Automation Testing:   https://zalo.me/g/lsxswc560
🌱 Facebook Group: Cộng đồng Automation Testing Việt Nam (Website, Desktop, Mobile)
🌱 Facebook Fanpage: Cộng đồng Automation Testing Việt Nam - Selenium

Chia sẻ kiến thức lên trang

Bạn có thể đăng bài để chia sẻ kiến thức, bài viết của chính bạn lên trang Anh Tester Blog

Danh sách bài học