반응형

개념

HtmlUnit이란?

  • 자바 기반의 headless 브라우저로, 웹페이지의 자동화 및 스크래핑을 위한 라이브러리를 말한다.
  • 서버측에서 실행되며 실제 브라우저를 사용하지 않고 HTML을 파싱하고 자바스크립트를 처리할 수 있다.

HtmlUnit VS Selenium

  • HtmlUnit
    • 자바 환경에서 지원한다.
    • 실제 브라우저를 사용하지 않는다.
    • 브라우저를 사용하지 않기 때문에 빠르고 페이지 로드가 효율적이라는 장점이 있다.
    • 자바스크립트 처리 지원이 제한적이며, 모든 JavaScript 기능을 지원하지 않을 수 있다는 단점이 있다.
  • Selenium
    • 자바 뿐만아니라 파이썬, 노드 등 다양한 환경에서 지원한다.
    • 실제 브라우저를 사용한다.
    • 브라우저를 사용하기 때문에 대부분의 JavaScript 기능을 지원 가능하고, 실제 브라우저 대상으로 UI 테스트가 가능하다는 장점이 있다.
    • 느리다는 단점이 있다.

예제

~/src/main/resources/static/demo.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>HtmlUnit Demo</title>
</head>
<body>
<div id="container"></div>
<script>
    // 페이지 로드 후 5초 뒤에 content 요소 추가
    setTimeout(() => {
        const content = document.createElement('div');
        content.textContent = "Hello World";
        content.classList.add('content');

        const container = document.querySelector('#container');
        container.appendChild(content);
    }, 5000);
</script>
</body>
</html>

build.gradle.kts

dependencies {
    // ...
    implementation("org.htmlunit:htmlunit:4.4.0")
}

HtmlUnitTest

class HtmlUnitTest {
    @Test
    fun testHtmlUnit() {
        val webClient = WebClient(BrowserVersion.CHROME)
        webClient.options.isCssEnabled = true
        webClient.options.isJavaScriptEnabled = true

        val page = webClient.getPage<HtmlPage>("http://localhost:8080/demo.html")

        waitUntilAdded(page, Duration.ofSeconds(15)) {
            it is HtmlElement && it.getAttribute("class").contains("content")
        }

        val content = page.querySelector<HtmlElement>(".content")
        val html = page.asXml()

        assertEquals("HtmlUnit Demo", page.titleText)
        assertEquals("Hello World", content.textContent)
        assertTrue(content.getAttribute("class").contains("content"))
        assertTrue(html.contains("Hello World"))
    }

    private fun waitUntilAdded(page: HtmlPage, duration: Duration, condition: (DomNode) -> Boolean) {
        val countDownLatch = CountDownLatch(1)
        val listener = object : DomChangeListener {
            override fun nodeAdded(event: DomChangeEvent) {
                if (condition(event.changedNode)) {
                    countDownLatch.countDown()
                }
            }

            override fun nodeDeleted(event: DomChangeEvent) {
                // do nothing
            }
        }

        page.addDomChangeListener(listener)
        countDownLatch.await(duration.toMillis(), TimeUnit.MILLISECONDS)
        page.removeDomChangeListener(listener)
    }
}
반응형

'Development > Java' 카테고리의 다른 글

[Java] Snowflake  (0) 2023.11.02
[Java] Java 소스 파일 컴파일하여 Class 객체로 로딩하기  (0) 2023.05.27
[Java] Mockito  (0) 2022.06.23
[Java] Random  (0) 2022.06.21
[Java] Cipher(RSA, AES)  (0) 2022.04.27

+ Recent posts