Introduction à HtmlUnit

1. Introduction

Dans cet article, nous présenterons HtmlUnit, un outil qui nous permet, en termes simples, d' interagir avec et de tester un site HTML par programmation, à l'aide d'API JAVA .

2. À propos de HtmlUnit

HtmlUnit est un navigateur sans interface graphique - un navigateur destiné à être utilisé par programme et non directement par un utilisateur.

Le navigateur prend en charge JavaScript (via le moteur Mozilla Rhino) et peut être utilisé même pour les sites Web dotés de fonctionnalités AJAX complexes. Tout cela peut être fait en simulant un navigateur basé sur une interface graphique typique comme Chrome ou Firefox.

Le nom HtmlUnit pourrait vous amener à penser qu'il s'agit d'un cadre de test, mais s'il peut certainement être utilisé pour des tests, il peut faire bien plus que cela.

Il a également été intégré à Spring 4 et peut être utilisé de manière transparente avec le framework Spring MVC Test.

3. Téléchargement et dépendance Maven

HtmlUnit peut être téléchargé depuis SourceForge ou depuis le site officiel. Vous pouvez également l'inclure dans votre outil de construction (comme Maven ou Gradle, entre autres) comme vous pouvez le voir ici. Par exemple, voici la dépendance Maven que vous pouvez actuellement inclure dans votre projet:

 net.sourceforge.htmlunit htmlunit 2.23  

La dernière version peut être trouvée ici.

4. Test Web

Il existe de nombreuses façons de tester une application Web - dont la plupart ont été abordées ici sur le site à un moment ou à un autre.

Avec HtmlUnit, vous pouvez directement analyser le HTML d'un site, interagir avec lui comme un utilisateur normal le ferait depuis le navigateur, vérifier la syntaxe JavaScript et CSS, soumettre des formulaires et analyser les réponses pour voir le contenu de ses éléments HTML. Tout cela, en utilisant du code Java pur.

Commençons par un simple test: créez un WebClient et récupérez la première page de la navigation de 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  

Vous pouvez voir des avertissements ou des erreurs lors de l'exécution de ce test si notre site Web a des problèmes JavaScript ou CSS. Vous devriez les corriger.

Parfois, si vous savez ce que vous faites (par exemple, si vous voyez que les seules erreurs que vous avez proviennent de bibliothèques JavaScript tierces que vous ne devez pas modifier), vous pouvez empêcher ces erreurs de faire échouer votre test, en appelant setThrowExceptionOnScriptError avec faux :

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

5. Raclage Web

Vous n'avez pas besoin d'utiliser HtmlUnit uniquement pour vos propres sites Web. C'est un navigateur, après tout: vous pouvez l'utiliser pour naviguer sur n'importe quel site Web que vous aimez, envoyer et récupérer des données selon vos besoins.

Récupérer, analyser, stocker et analyser des données à partir de sites Web est le processus connu sous le nom de web scraping et HtmlUnit peut vous aider avec la récupération et l'analyse des parties.

L'exemple précédent montre comment nous pouvons accéder à n'importe quel site Web et y naviguer, en récupérant toutes les informations souhaitées.

Par exemple, allons aux archives complètes des articles de Baeldung, naviguons vers le dernier article et récupérons son titre (premier

marque). Pour notre test, cela suffira; mais, si nous voulions stocker plus d'informations, nous pourrions, par exemple, récupérer les en-têtes (tous

balises) ainsi que d'avoir une idée de base de l'objet de l'article.

Il est facile d'obtenir des éléments par leur ID, mais en général, si vous avez besoin de trouver un élément, il est plus pratique d' utiliser la syntaxe XPath . HtmlUnit nous permet de l'utiliser, donc nous le ferons.

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

Notez d'abord comment - dans ce cas, nous ne sommes pas intéressés par CSS ni JavaScript et voulons juste analyser la mise en page HTML, nous avons donc désactivé CSS et JavaScript.

Dans un vrai web scraping, vous pourriez prendre par exemple les titres h1 et h2 , et le résultat serait quelque chose comme ceci:

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

Vous pouvez vérifier que les informations récupérées correspondent bien au dernier article de Baeldung:

6. Et AJAX?

Les fonctionnalités AJAX peuvent être un problème car HtmlUnit récupère généralement la page avant la fin des appels AJAX. Plusieurs fois, vous en avez besoin pour finir de tester correctement votre site Web ou pour récupérer les données souhaitées. Il existe plusieurs moyens de les gérer:

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

Il s'intègre parfaitement à Spring 4 et, associé au framework Spring MVC Test, vous offre un environnement très puissant pour effectuer des tests d'intégration de toutes vos pages, même sans serveur Web.

En outre, en utilisant HtmlUnit, vous pouvez automatiser toute tâche liée à la navigation Web, telle que la récupération, l'analyse, le stockage et l'analyse de données (Web scraping).

Vous pouvez récupérer le code sur Github.