암묵적 대기는 대기 시간을 전역적으로 정의할 때 사용하는 Wait의 종류이다.
우리는 일반적으로 새로운 웹 페이지에 접속하거나 특정 요소를 클릭했을 때 페이지를 로드하는 과정을 겪게 된다. 만약 페이지가 로드되지 않았는데 특정 엘리먼트를 식별하려 한다면 에러가 발생하게 되는데 이를 해결할 방법이 암묵적 대기이다.
암묵적 대기는 웹 드라이버를 통해 엘리먼트를 식별하거나 클릭 등의 액션을 시도할 때 오류를 발생시키기 전에 n 초까지 기다리라고 명령하는 방식이다. 드바이버 생성 후 전역적으로 한 번만 선언해주면 드라이버 객체가 소멸할 때까지 전역적으로 적용된다.
그렇다고 매 엘리먼트를 식별할 때마다 무조건 n 초를 대기하는 것이 아니다. 암묵적 대기 상태에서 계속 DOM을 읽어오고 페이지가 로드되어 엘리먼트를 인식했다면 n 초가 지나기 전에 다음 코드를 수행하게 된다. 다시 말해 암묵적 대기 시간을 10초로 설정했으나, 페이지가 2초 만에 로드되어 엘리먼트를 식별했다면 루프를 빠져나와 다음 코드를 실행하게 된다는 의미이다.
// 암묵적 대기
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import java.time.Duration;
public class Waitdemo {
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
}
}
명시적 대기는 대기 시간을 명시적으로 정의할 때 사용하는 Wait의 종류이다.
로드하는데 10~13초 정도 소요되는 특정 페이지가 있다고 가정해보자. 이 페이지를 위해 암묵적 대기 시간을 전역적으로 15초로 설정하게 되면 테스트 케이스는 성공하겠지만 좋은 구현 방법이 아니다.
그 첫 번째 이유로는 테스트 수행 시간이 늘어나게 된다. 특정 엘리먼트를 찾는 10개의 테스트 케이스가 존재한다고 가정해보자. 만약, 10개의 테스트 케이스가 모두 실패하는 경우라면 15초 * 10 = 150초의 테스트 시간이 소요된다. 이처럼 암묵적 대기는 전역적으로 선언하여 사용하는 방식이기 때문에 테스트 케이스가 늘어나면 늘어날수록, 실패하는 케이스가 많으면 많을수록 더 많은 시간을 소모하게 된다.
두 번째, 성능 이슈를 필터할 수 없다. 100개의 페이지 중 99개의 페이지는 5초 이내에 로드되어야 하는 페이지이고 1개의 페이지만 10~13초 정도 소요되는 페이지라고 가정해보자. 후자의 테스트케이스를 위해 암묵적 대기를 15초로 설정해버리면 나머지 99개의 페이지의 로드 시간이 늘어났을 때 성능 이슈를 탐지할 수 없다.
이런 상황에서 활용할 수 있는 Wait의 종류가 바로 명시적 대기이다. 명시적 대기는 특정 요소, 특정 시나리오를 타겟팅하여 대기 시간을 설정할 수 있다. 예를 들어 "A"라는 엘리먼트를 찾는데 15초 정도 소요된다고 가정해보면 이 요소, 이 시나리오에만 최대 15초의 대기 시간을 줄 수 있다는 것이다.
명시적 대기는 크게 두가지 방법으로 분류할 수 있는데, 그중 첫 번째 방법은 웹 드라이버를 활용하는 방법이다. WebDriverWait
객체를 생성하고 loacator를 인자로 넘겨주면 된다.
// 웹 드라이버 명시적 대기
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
public class Waitdemo {
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("span.promoInfo"))).click();
}
}
웹 드라이버 명시적 대기는 코드가 직관적이지만, Fluent Wait는 코드가 매우 지저분하다. 또한 일반적으로 잘 사용하지 않는 방식이라 가볍게 알아보고 넘어가자. Fluent Wait는 엘리먼트를 식별하기까지 최대 대기하는 시간과 엘리먼트를 식별하는 반복 주기 등을 정의할 수 있다.
예를 들어, 특정 엘리먼트를 식별하기 위해 최대 30초 동안 대기하는데 5초마다 엘리먼트를 체크하고 싶다면 아래와 같이 코드를 작성하면 된다. 다만 Fluent Wait는 아래 예제처럼 코드가 지저분해지기 때문에 용도에 따라서 적절히 활용하면 될 것 같다.
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Wait;
import java.time.Duration;
import java.util.function.Function;
public class Waitdemo {
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout(Duration.ofSeconds(30))
.pollingEvery(Duration.ofSeconds(5))
.ignoring(NoSuchElementException.class);
WebElement e = wait.until(new Function<WebDriver, WebElement>() {
@Override
public WebElement apply(WebDriver driver) {
return driver.findElement(By.cssSelector("span.promoInfo"));
}
});
}
}
Thread.Sleep(Time.sleep)은 프로그래밍 언어에서 지원하는 Sleep 메서드를 통해 스크립트를 일정시간 동안 정지하는 방식이다. 만약 sleep 시간을 10초로 주었다면 3초만에 엘리먼트가 등장했음에도 무조건 10초를 기다리게 된다. 따라서 권장되는 방식은 아니다.