Einführung in HtmlUnit

1. Einleitung

In diesem Artikel stellen wir HtmlUnit vor, ein Tool, mit dem wir eine HTML-Site mithilfe von JAVA-APIs programmgesteuert einfach einfügen, interagieren und testen können .

2. Über HtmlUnit

HtmlUnit ist ein Browser ohne GUI - ein Browser, der programmgesteuert und nicht direkt von einem Benutzer verwendet werden soll.

Der Browser unterstützt JavaScript (über die Mozilla Rhino-Engine) und kann auch für Websites mit komplexen AJAX-Funktionen verwendet werden. All dies kann durch Simulation eines typischen GUI-basierten Browsers wie Chrome oder Firefox erfolgen.

Der Name HtmlUnit könnte Sie glauben machen, dass es sich um ein Testframework handelt, aber obwohl es definitiv zum Testen verwendet werden kann, kann es noch viel mehr.

Es wurde auch in Spring 4 integriert und kann nahtlos zusammen mit dem Spring MVC Test Framework verwendet werden.

3. Download und Maven-Abhängigkeit

HtmlUnit kann von SourceForge oder von der offiziellen Website heruntergeladen werden. Sie können es auch in Ihr Bauwerkzeug aufnehmen (z. B. Maven oder Gradle), wie Sie hier sehen können. Dies ist beispielsweise die Maven-Abhängigkeit, die Sie derzeit in Ihr Projekt aufnehmen können:

 net.sourceforge.htmlunit htmlunit 2.23  

Die neueste Version finden Sie hier.

4. Webtests

Es gibt viele Möglichkeiten, wie Sie eine Webanwendung testen können - die meisten davon haben wir hier auf der Website zu dem einen oder anderen Zeitpunkt behandelt.

Mit HtmlUnit können Sie den HTML-Code einer Site direkt analysieren, wie ein normaler Benutzer über den Browser damit interagieren, die JavaScript- und CSS-Syntax überprüfen, Formulare senden und die Antworten analysieren, um den Inhalt der HTML-Elemente anzuzeigen. Alles mit reinem Java-Code.

Beginnen wir mit einem einfachen Test: Erstellen Sie einen WebClient und rufen Sie die erste Seite der Navigation von www.baeldung.com auf :

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  

Sie können einige Warnungen oder Fehler sehen, wenn Sie diesen Test ausführen, wenn unsere Website JavaScript- oder CSS-Probleme aufweist. Sie sollten sie korrigieren.

Wenn Sie wissen, was Sie tun (z. B. wenn Sie feststellen, dass die einzigen Fehler, die Sie haben, aus JavaScript-Bibliotheken von Drittanbietern stammen, die Sie nicht ändern sollten), können Sie manchmal verhindern, dass diese Fehler Ihren Test zum Scheitern bringen, indem Sie setThrowExceptionOnScriptError mit aufrufen false :

@Test public void givenAClient_whenEnteringBaeldung_thenPageTitleIsCorrect() throws Exception  Java, Spring and Web Development tutorials", page.getTitleText()); 

5. Web Scraping

Sie müssen HtmlUnit nicht nur für Ihre eigenen Websites verwenden. Immerhin handelt es sich um einen Browser: Sie können damit durch jedes gewünschte Web navigieren, Daten nach Bedarf senden und abrufen.

Das Abrufen, Parsen, Speichern und Analysieren von Daten von Websites wird als Web Scraping bezeichnet. HtmlUnit kann Ihnen beim Abrufen und Parsen von Teilen helfen.

Das vorherige Beispiel zeigt, wie wir eine Website aufrufen und durch sie navigieren können, um alle gewünschten Informationen abzurufen.

Gehen wir zum Beispiel zum vollständigen Artikelarchiv von Baeldung, navigieren Sie zum neuesten Artikel und rufen Sie dessen Titel ab (zuerst)

Etikett). Für unseren Test wird das ausreichen; Wenn wir jedoch mehr Informationen speichern möchten, können wir beispielsweise die Überschriften (alle) abrufen

Tags), so dass Sie eine grundlegende Vorstellung davon haben, worum es in dem Artikel geht.

Es ist einfach, Elemente anhand ihrer ID abzurufen. Wenn Sie jedoch ein Element suchen müssen, ist es im Allgemeinen bequemer, die XPath-Syntax zu verwenden . Mit HtmlUnit können wir es verwenden, also werden wir es tun.

@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 h1 = (List) postPage.getByXPath("//h1"); Assert.assertTrue(h1.size() > 0); } 

Beachten Sie zunächst, wie - in diesem Fall interessieren wir uns weder für CSS noch für JavaScript und möchten nur das HTML-Layout analysieren. Deshalb haben wir CSS und JavaScript deaktiviert.

In einem echten Web-Scraping könnten Sie zum Beispiel die Titel h1 und h2 nehmen , und das Ergebnis wäre ungefähr so:

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

Sie können überprüfen, ob die abgerufenen Informationen tatsächlich dem neuesten Artikel in Baeldung entsprechen:

6. Was ist mit AJAX?

AJAX-Funktionen können ein Problem sein, da HtmlUnit die Seite normalerweise abruft, bevor die AJAX-Aufrufe abgeschlossen sind. Oft müssen sie fertig sein, um Ihre Website ordnungsgemäß zu testen oder die gewünschten Daten abzurufen. Es gibt einige Möglichkeiten, mit ihnen umzugehen:

  • You can use webClient.setAjaxController(new NicelyResynchronizingAjaxController()). This resynchronizes calls performed from the main thread and these calls are performed synchronously to ensure that there is a stable state to test.
  • When entering a page of a web application, you can wait for some seconds so there is enough time to let AJAX calls finish. To achieve this, you can use webClient.waitForBackgroundJavaScript(MILLIS) or webClient.waitForBackgroundJavaScriptStartingBefore(MILLIS). You should call them after retrieving the page, but before working with it.
  • You can wait until some expected condition related to the execution of the AJAX call is met. For instance:
for (int i = 0; i < 20; i++) { if (condition_to_happen_after_js_execution) { break; } synchronized (page) { page.wait(500); } }
  • Instead of creating a new WebClient(), that defaults to the best-supported web browser, try other browsers since they might work better with your JavaScript or AJAX calls. For instance, this will create a webClient that uses a Chrome browser:
WebClient webClient = new WebClient(BrowserVersion.CHROME);

7. An Example With Spring

If we're testing our own Spring application, then things get a little bit easier – we no longer need a running server.

Let's implement a very simple example app: just a controller with a method that receives a text, and a single HTML page with a form. The user can input a text into the form, submit the form, and the text will be shown below that form.

In this case, we'll use a Thymeleaf template for that HTML page (you can see a complete Thymeleaf example here):

@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 = "//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); } }

The key here is building the WebClient object using MockMvcWebClientBuilder from the WebApplicationContext. With the WebClient, we can get the first page of the navigation (notice how it's served by localhost), and start browsing from there.

As you can see, the test parses the form enters a message (in a field with ID “message”), submits the form and, on the new page, it asserts that the received text (field with ID “received”) is the same as the text we submitted.

8. Conclusion

HtmlUnit is a great tool that allows you to test your web applications easily, filling forms fields and submitting them just as if you were using the web on a browser.

Es lässt sich nahtlos in Spring 4 integrieren und bietet Ihnen zusammen mit dem Spring MVC Test Framework eine sehr leistungsstarke Umgebung, um Integrationstests für alle Ihre Seiten auch ohne Webserver durchzuführen.

Mit HtmlUnit können Sie auch alle Aufgaben im Zusammenhang mit dem Surfen im Internet automatisieren, z. B. das Abrufen, Parsen, Speichern und Analysieren von Daten (Web Scraping).

Sie können den Code auf Github übertragen.