Introduzione a HtmlUnit

1. Introduzione

In questo articolo, introdurremo HtmlUnit, uno strumento che ci consente, in poche parole, di interagire e testare un sito HTML in modo programmatico, utilizzando le API JAVA .

2. Informazioni su HtmlUnit

HtmlUnit è un browser senza GUI, un browser progettato per essere utilizzato a livello di programmazione e non direttamente da un utente.

Il browser supporta JavaScript (tramite il motore Mozilla Rhino) e può essere utilizzato anche per siti Web con funzionalità AJAX complesse. Tutto questo può essere fatto simulando un tipico browser basato su GUI come Chrome o Firefox.

Il nome HtmlUnit potrebbe farti pensare che sia un framework di test, ma sebbene possa essere sicuramente utilizzato per i test, può fare molto di più.

È stato anche integrato in Spring 4 e può essere utilizzato senza problemi insieme al framework Spring MVC Test.

3. Download e dipendenza da Maven

HtmlUnit può essere scaricato da SourceForge o dal sito ufficiale. Inoltre, puoi includerlo nel tuo strumento di costruzione (come Maven o Gradle, tra gli altri) come puoi vedere qui. Ad esempio, questa è la dipendenza Maven che puoi attualmente includere nel tuo progetto:

 net.sourceforge.htmlunit htmlunit 2.23  

La versione più recente può essere trovata qui.

4. Test web

Esistono molti modi in cui è possibile testare un'applicazione Web, la maggior parte dei quali è stata trattata qui sul sito in un punto o in un altro.

Con HtmlUnit puoi analizzare direttamente l'HTML di un sito, interagire con esso proprio come farebbe un normale utente dal browser, controllare la sintassi JavaScript e CSS, inviare moduli e analizzare le risposte per vedere il contenuto dei suoi elementi HTML. Tutto questo, utilizzando puro codice Java.

Cominciamo con un semplice test: crea un WebClient e ottieni la prima pagina di navigazione di 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  

Puoi visualizzare alcuni avvisi o errori durante l'esecuzione di quel test se il nostro sito web presenta problemi con JavaScript o CSS. Dovresti correggerli.

A volte, se sai cosa stai facendo (ad esempio, se vedi che gli unici errori che hai provengono da librerie JavaScript di terze parti che non dovresti modificare) puoi impedire a questi errori di far fallire il tuo test, chiamando setThrowExceptionOnScriptError con falso :

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

5. Web Scraping

Non è necessario utilizzare HtmlUnit solo per i propri siti Web. Dopotutto è un browser: puoi usarlo per navigare attraverso qualsiasi web che ti piace, inviare e recuperare i dati secondo necessità.

Il recupero, l'analisi, l'archiviazione e l'analisi dei dati dai siti Web è il processo noto come web scraping e HtmlUnit può aiutarti con il recupero e l'analisi delle parti.

L'esempio precedente mostra come possiamo entrare in qualsiasi sito web e navigare attraverso di esso, recuperando tutte le informazioni che vogliamo.

Ad esempio, andiamo all'archivio completo di articoli di Baeldung, passiamo all'ultimo articolo e recuperiamo il suo titolo (primo

etichetta). Per il nostro test, sarà sufficiente; ma, se volessimo memorizzare più informazioni, potremmo, ad esempio, recuperare i titoli (all

tag), avendo così un'idea di base di cosa tratta l'articolo.

È facile ottenere elementi tramite il loro ID, ma generalmente, se devi trovare un elemento, è più conveniente usare la sintassi XPath . HtmlUnit ci permette di usarlo, quindi lo faremo.

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

Prima nota come: in questo caso, non siamo interessati a CSS né JavaScript e vogliamo solo analizzare il layout HTML, quindi abbiamo disattivato CSS e JavaScript.

In un vero web scraping, potresti prendere ad esempio i titoli h1 e h2 e il risultato sarebbe qualcosa del genere:

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

Puoi verificare che le informazioni recuperate corrispondano effettivamente all'ultimo articolo di Baeldung:

6. E AJAX?

Le funzionalità AJAX possono essere un problema perché HtmlUnit di solito recupera la pagina prima che le chiamate AJAX siano terminate. Molte volte ne hai bisogno per finire per testare correttamente il tuo sito web o per recuperare i dati che desideri. Ci sono alcuni modi per affrontarli:

  • È possibile utilizzare webClient.setAjaxController (new NicelyResynchronizingAjaxController ()) . Ciò risincronizza le chiamate eseguite dal thread principale e queste chiamate vengono eseguite in modo sincrono per garantire che vi sia uno stato stabile da testare.
  • Quando si accede a una pagina di un'applicazione Web, è possibile attendere alcuni secondi in modo che ci sia tempo sufficiente per terminare le chiamate AJAX. Per ottenere ciò, è possibile utilizzare webClient.waitForBackgroundJavaScript (MILLIS) o webClient.waitForBackgroundJavaScriptStartingBefore (MILLIS) . Dovresti chiamarli dopo aver recuperato la pagina, ma prima di lavorarci.
  • È possibile attendere fino a quando non vengono soddisfatte alcune condizioni previste relative all'esecuzione della chiamata AJAX. Per esempio:
for (int i = 0; i < 20; i++) { if (condition_to_happen_after_js_execution) { break; } synchronized (page) { page.wait(500); } }
  • Invece di creare un nuovo WebClient () , che utilizza per impostazione predefinita il browser web meglio supportato, prova altri browser poiché potrebbero funzionare meglio con le tue chiamate JavaScript o AJAX. Ad esempio, questo creerà un webClient che utilizza un browser Chrome:
WebClient webClient = new WebClient(BrowserVersion.CHROME);

7. Un esempio con la primavera

Se stiamo testando la nostra applicazione Spring, le cose diventano un po 'più semplici: non abbiamo più bisogno di un server in esecuzione .

Implementiamo un'app di esempio molto semplice: solo un controller con un metodo che riceve un testo e una singola pagina HTML con un modulo. L'utente può inserire un testo nel modulo, inviare il modulo e il testo verrà mostrato sotto quel modulo.

In questo caso, useremo un modello Thymeleaf per quella pagina HTML (puoi vedere un esempio completo di Thymeleaf qui):

@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.

Si integra perfettamente con Spring 4 e, insieme al framework Spring MVC Test, ti offrono un ambiente molto potente per eseguire test di integrazione di tutte le tue pagine anche senza un server web.

Inoltre, utilizzando HtmlUnit è possibile automatizzare qualsiasi attività relativa alla navigazione web, come il recupero, l'analisi, l'archiviazione e l'analisi dei dati (web scraping).

Puoi ottenere il codice su Github.