1. 序章

この記事では、HtmlUnitを紹介します。これは、JAVA API を使用して、プログラムでHTMLサイトと対話およびテストできるツールです。

2. HtmlUnitについて

HtmlUnit は、GUIを使用しないブラウザーであり、ユーザーが直接使用するのではなく、プログラムで使用することを目的としたブラウザーです。

ブラウザはJavaScriptをサポートし( Mozilla Rhino エンジン経由)、複雑なAJAX機能を備えたWebサイトでも使用できます。 これらはすべて、ChromeやFirefoxなどの一般的なGUIベースのブラウザをシミュレートして実行できます。

HtmlUnitという名前は、それがテストフレームワークであると思わせるかもしれませんが、テストには間違いなく使用できますが、それ以上のことを行うことができます。

また、 Spring 4 に統合されており、SpringMVCテストフレームワークとシームレスに使用できます。

3. ダウンロードとMavenの依存関係

HtmlUnitは、SourceForgeまたは公式Webサイトからダウンロードできます。 また、ここに表示されているように、ビルドツール(MavenやGradleなど)に含めることもできます。 たとえば、これは現在プロジェクトに含めることができるMavenの依存関係です。

<dependency>
    <groupId>net.sourceforge.htmlunit</groupId>
    <artifactId>htmlunit</artifactId>
    <version>2.23</version>
</dependency>

最新バージョンはここにあります。

4. Webテスト

Webアプリケーションをテストする方法はたくさんありますが、そのほとんどは、このサイトのどこかで取り上げました。

HtmlUnitを使用すると、サイトのHTMLを直接解析し、通常のユーザーがブラウザーから行うのと同じように操作し、JavaScriptとCSSの構文を確認し、フォームを送信して応答を解析し、HTML要素のコンテンツを確認できます。 すべて、純粋なJavaコードを使用しています。

簡単なテストから始めましょう。WebClientを作成し、www.baeldung.comのナビゲーションの最初のページを取得します。

private WebClient webClient;

@Before
public void init() throws Exception {
    webClient = new WebClient();
}

@After
public void close() throws Exception {
    webClient.close();
}

@Test
public void givenAClient_whenEnteringBaeldung_thenPageTitleIsOk()
  throws Exception {
    HtmlPage page = webClient.getPage("/");
    
    Assert.assertEquals(
      "Baeldung | Java, Spring and Web Development tutorials",
        page.getTitleText());
}

ウェブサイトにJavaScriptまたはCSSの問題がある場合、そのテストを実行すると、いくつかの警告またはエラーが表示されます。 それらを修正する必要があります。

場合によっては、自分が何をしているのかがわかっている場合(たとえば、変更してはならないサードパーティのJavaScriptライブラリからのエラーだけが表示されている場合)、[X232Xを呼び出して、これらのエラーによってテストが失敗するのを防ぐことができます。 ] setThrowExceptionOnScriptError with false

@Test
public void givenAClient_whenEnteringBaeldung_thenPageTitleIsCorrect()
  throws Exception {
    webClient.getOptions().setThrowExceptionOnScriptError(false);
    HtmlPage page = webClient.getPage("/");
    
    Assert.assertEquals(
      "Baeldung | Java, Spring and Web Development tutorials",
        page.getTitleText());
}

5. Webスクレイピング

自分のWebサイトのためだけにHtmlUnitを使用する必要はありません。 結局のところ、これはブラウザです。これを使用して、好きなWebをナビゲートし、必要に応じてデータを送信および取得できます。

Webサイトからのデータの取得、解析、保存、および分析はWebスクレイピングと呼ばれるプロセスであり、HtmlUnitはパーツの取得と解析を支援します。

前の例は、任意のWebサイトにアクセスしてナビゲートし、必要なすべての情報を取得する方法を示しています。

たとえば、Baeldungの記事の完全なアーカイブに移動し、最新の記事に移動して、そのタイトルを取得しましょう(最初に

鬼ごっこ)。 私たちのテストでは、それで十分です。 ただし、より多くの情報を保存したい場合は、たとえば、見出しを取得できます(すべて

タグ)も同様に、したがって、記事が何であるかについての基本的な考えを持っています。

IDで要素を取得するのは簡単ですが、一般に、要素を検索する必要がある場合は、XPath構文を使用する方が便利です。 HtmlUnitを使用すると使用できるので、使用します。

@Test
public void givenBaeldungArchive_whenRetrievingArticle_thenHasH1() 
  throws Exception {
    webClient.getOptions().setCssEnabled(false);
    webClient.getOptions().setJavaScriptEnabled(false);

    String url = "/full_archive";
    HtmlPage page = webClient.getPage(url);
    String xpath = "(//ul[@class='car-monthlisting']/li)[1]/a";
    HtmlAnchor latestPostLink 
      = (HtmlAnchor) page.getByXPath(xpath).get(0);
    HtmlPage postPage = latestPostLink.click();

    List<HtmlHeading1> h1  
      = (List<HtmlHeading1>) postPage.getByXPath("//h1");
 
    Assert.assertTrue(h1.size() > 0);
}

まず、どのように注意してください。この場合、CSSにもJavaScriptにも関心がなく、HTMLレイアウトを解析したいだけなので、CSSとJavaScriptをオフにしました。

実際のWebスクレイピングでは、たとえばh1およびh2のタイトルを取ることができ、結果は次のようになります。

Java Web Weekly, Issue 135
1. Spring and Java
2. Technical and Musings
3. Comics
4. Pick of the Week

取得した情報が実際にBaeldungの最新の記事に対応していることを確認できます。

6. AJAXはどうですか?

HtmlUnitは通常、AJAX呼び出しが終了する前にページを取得するため、AJAX機能が問題になる可能性があります。 多くの場合、Webサイトを適切にテストしたり、必要なデータを取得したりするには、それらを終了する必要があります。 それらに対処するいくつかの方法があります:

  • webClient.setAjaxController(new NicelyResynchronizingAjaxController())を使用できます。 これにより、メインスレッドから実行された呼び出しが再同期され、これらの呼び出しが同期的に実行されて、テストする安定した状態が確保されます。
  • Webアプリケーションのページに入るときは、AJAX呼び出しが終了するのに十分な時間があるように、数秒待つことができます。 これを実現するには、 webClient.waitForBackgroundJavaScript(MILLIS)または webClient.waitForBackgroundJavaScriptStartingBefore(MILLIS)を使用できます。 ページを取得した後、作業する前に呼び出す必要があります。
  • AJAX呼び出しの実行に関連するいくつかの予想される条件が満たされるまで待つことができます。 例えば:
for (int i = 0; i < 20; i++) {
    if (condition_to_happen_after_js_execution) {
        break;
    }
    synchronized (page) {
        page.wait(500);
    }
}
  • new WebClient()を作成する代わりに、デフォルトで最もサポートされているWebブラウザーを使用します。他のブラウザーを試してみてください。これは、JavaScriptまたはAJAX呼び出しでより適切に機能する可能性があるためです。 たとえば、これによりChromeブラウザを使用するwebClientが作成されます。
WebClient webClient = new WebClient(BrowserVersion.CHROME);

7. 春の例

独自のSpringアプリケーションをテストしている場合は、状況が少し簡単になります。実行中のサーバーは不要になります。

非常に単純なサンプルアプリを実装しましょう。テキストを受け取るメソッドを備えたコントローラーと、フォームを備えた単一のHTMLページです。 ユーザーはフォームにテキストを入力してフォームを送信できます。テキストはそのフォームの下に表示されます。

この場合、そのHTMLページに Thymeleaf テンプレートを使用します(完全なThymeleafの例ここを参照できます):

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = { TestConfig.class })
public class HtmlUnitAndSpringTest {

    @Autowired
    private WebApplicationContext wac;

    private WebClient webClient;

    @Before
    public void setup() {
        webClient = MockMvcWebClientBuilder
          .webAppContextSetup(wac).build();
    }

    @Test
    public void givenAMessage_whenSent_thenItShows() throws Exception {
        String text = "Hello world!";
        HtmlPage page;

        String url = "http://localhost/message/showForm";
        page = webClient.getPage(url);
            
        HtmlTextInput messageText = page.getHtmlElementById("message");
        messageText.setValueAttribute(text);

        HtmlForm form = page.getForms().get(0);
        HtmlSubmitInput submit = form.getOneHtmlElementByAttribute(
          "input", "type", "submit");
        HtmlPage newPage = submit.click();

        String receivedText = newPage.getHtmlElementById("received")
            .getTextContent();

        Assert.assertEquals(receivedText, text);     
    }
}

ここで重要なのは、WebApplicationContextからMockMvcWebClientBuilderを使用してWebClientオブジェクトを構築することです。 WebClient を使用すると、ナビゲーションの最初のページを取得し( localhost によってどのように提供されるかに注意してください)、そこからブラウジングを開始できます。

ご覧のとおり、テストはフォームを解析して(ID「message」のフィールドに)メッセージを入力し、フォームを送信し、新しいページで、受信したテキスト(ID「received」のフィールド)が提出したテキストと同じです。

8. 結論

HtmlUnitは、Webアプリケーションを簡単にテストし、フォームフィールドに入力して、ブラウザでWebを使用しているかのように送信できる優れたツールです。

Spring 4とシームレスに統合され、Spring MVC Testフレームワークと組み合わせることで、Webサーバーがなくてもすべてのページの統合テストを行うための非常に強力な環境が提供されます。

また、HtmlUnitを使用すると、データのフェッチ、解析、保存、分析(Webスクレイピング)など、Webブラウジングに関連するすべてのタスクを自動化できます。

コードはGithubで入手できます。