Einführung in HtmlUnit

Einführung in HtmlUnit

1. Einführung

In diesem Artikel stellen wir HtmlUnit vor, ein Tool, mit dem wir einfachinteract with and test an HTML site programmatically, using JAVA APIs eingeben 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 dieMozilla Rhino-Engine) und kann auch für Websites mit komplexen AJAX-Funktionen verwendet werden. All dies kann mit einem typischen GUI-basierten Browser wie Chrome oder Firefox simuliert werden.

Der Name HtmlUnit könnte Sie zu der Annahme führen, dass es sich um ein Testframework handelt, aber obwohl es definitiv zum Testen verwendet werden kann, kann es noch viel mehr.

Es war auchintegrated into Spring 4 und kann nahtlos zusammen mit dem Spring MVC Test Framework verwendet werden.

3. Download und Maven-Abhängigkeit

HtmlUnit kanndownloaded from SourceForge oderofficial website sein. Sie können es auch in Ihr Bauwerkzeug aufnehmen (z. B. Maven oder Gradle), da Siehere 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 inhere.

4. Web-Tests

Es gibt viele Möglichkeiten, eine Webanwendung zu testen - die meisten davon haben wir hier auf der Website an der einen oder anderen Stelle behandelt.

Mit HtmlUnit können Sie den HTML-Code einer Website direkt analysieren, wie ein normaler Benutzer über den Browser mit ihr 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 einWebClient und rufen Sie die erste Seite der Navigation vonwww.example.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_whenEnteringexample_thenPageTitleIsOk()
  throws Exception {
    HtmlPage page = webClient.getPage("/");

    Assert.assertEquals(
      "example | Java, Spring and Web Development tutorials",
        page.getTitleText());
}

Bei der Ausführung dieses Tests werden einige Warnungen oder Fehler angezeigt, wenn auf unserer Website JavaScript- oder CSS-Probleme auftreten. Sie sollten sie korrigieren.

Wenn Sie wissen, was Sie tun (wenn Sie beispielsweise 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 dazu führen, dass Ihr Test fehlschlägt, indem Sie{'t0': 'setThrowExceptionOnScriptError', 't1': 'false'}ufrufen ( t0) s mitfalse:

@Test
public void givenAClient_whenEnteringexample_thenPageTitleIsCorrect()
  throws Exception {
    webClient.getOptions().setThrowExceptionOnScriptError(false);
    HtmlPage page = webClient.getPage("/");

    Assert.assertEquals(
      "example | 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 ihn verwenden, um durch ein beliebiges Web zu navigieren, Daten nach Bedarf zu senden und abzurufen.

Das Abrufen, Parsen, Speichern und Analysieren von Daten von Websites wird als Web-Scraping bezeichnet. HtmlUnit unterstützt Sie beim Abrufen und Parsen von Teilen.

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

Gehen wir zum Beispiel zum vollständigen Artikelarchiv des Beispiels, navigieren Sie zum neuesten Artikel und rufen Sie dessen Titel ab (erstes<h1>-Tag). Für unseren Test wird das ausreichen; Wenn wir jedoch mehr Informationen speichern möchten, können wir beispielsweise auch die Überschriften (alle<h2>-Tags) abrufen und so eine grundlegende Vorstellung davon bekommen, worum es in dem Artikel geht.

Es ist einfach, Elemente anhand ihrer ID zu ermitteln. Wenn Sie jedoch ein Element suchen müssen, ist es füruse XPath syntax im Allgemeinen bequemer. HtmlUnit erlaubt es uns, es zu benutzen, so werden wir es tun.

@Test
public void givenexampleArchive_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 Titelh1 undh2nehmen, 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 im Beispiel entsprechen:

image

6. Was ist mit AJAX?

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

  • Sie könnenwebClient.setAjaxController(new NicelyResynchronizingAjaxController()) verwenden. Dadurch werden die vom Hauptthread ausgeführten Aufrufe erneut synchronisiert. Diese Aufrufe werden synchron ausgeführt, um sicherzustellen, dass ein stabiler zu testender Zustand vorliegt.

  • Wenn Sie eine Seite einer Webanwendung aufrufen, können Sie einige Sekunden warten, damit genügend Zeit bleibt, um AJAX-Aufrufe zu beenden. Um dies zu erreichen, können SiewebClient.waitForBackgroundJavaScript(MILLIS) oderwebClient.waitForBackgroundJavaScriptStartingBefore(MILLIS) verwenden. Sie sollten sie aufrufen, nachdem Sie die Seite abgerufen haben, aber bevor Sie damit arbeiten.

  • Sie können warten, bis eine erwartete Bedingung für die Ausführung des AJAX-Aufrufs erfüllt ist. Zum Beispiel:

for (int i = 0; i < 20; i++) {
    if (condition_to_happen_after_js_execution) {
        break;
    }
    synchronized (page) {
        page.wait(500);
    }
}
  • Anstatt einnew WebClient() zu erstellen, das standardmäßig den am besten unterstützten Webbrowser verwendet, versuchen Sie es mit anderen Browsern, da diese möglicherweise besser mit Ihren JavaScript- oder AJAX-Aufrufen funktionieren. So wird beispielsweise ein WebClient erstellt, der einen Chrome-Browser verwendet:

WebClient webClient = new WebClient(BrowserVersion.CHROME);

7. Ein Beispiel mit Frühling

Wenn wir unsere eigene Spring-Anwendung testen, wird es etwas einfacher -we no longer need a running server.

Implementieren wir eine sehr einfache Beispiel-App: nur einen Controller mit einer Methode, die einen Text empfängt, und eine einzelne HTML-Seite mit einem Formular. Der Benutzer kann einen Text in das Formular eingeben, das Formular abschicken und der Text wird unter diesem Formular angezeigt.

In diesem Fall verwenden wir eineThymeleaf-Vorlage für diese HTML-Seite (Sie können ein vollständiges Thymeleaf-Beispielhere sehen):

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

Der Schlüssel hier ist das Erstellen desWebClient-Objekts unter Verwendung vonMockMvcWebClientBuilder ausWebApplicationContext. MitWebClient können wir die erste Seite der Navigation abrufen (beachten Sie, wie sie vonlocalhost bedient wird) und von dort aus mit dem Surfen beginnen.

Wie Sie sehen, analysiert der Test, ob das Formular eine Nachricht eingibt (in einem Feld mit der ID "Nachricht"), sendet das Formular ab und bestätigt auf der neuen Seite, dass der empfangene Text (Feld mit der ID "empfangen") der ist genauso wie der von uns eingereichte Text.

8. Fazit

HtmlUnit ist ein großartiges Tool, mit dem Sie Ihre Webanwendungen einfach testen, Formularfelder ausfüllen und senden können, als würden Sie das Web in einem Browser verwenden.

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 einen Webserver durchzuführen.

Mit HtmlUnit können Sie außerdem 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 Codeover on Github erhalten.