Introduction à HtmlUnit

Introduction à HtmlUnit

1. introduction

Dans cet article, nous allons vous présenter HtmlUnit, un outil qui nous permet, en termes simples, deinteract with and test an HTML site programmatically, using JAVA APIs.

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 moteurMozilla 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 typique basé sur une interface graphique, comme Chrome ou Firefox.

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

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

3. Dépendance de téléchargement et de Maven

HtmlUnit peut êtredownloaded from SourceForge ou desofficial website. Vous pouvez également l'inclure dans votre outil de construction (comme Maven ou Gradle, entre autres) comme vous pouvez le voirhere. Par exemple, il s’agit de 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éehere.

4. Test Web

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

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

Commençons par un test simple: créez unWebClient et récupérez la première page de la navigation dewww.example.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_whenEnteringexample_thenPageTitleIsOk()
  throws Exception {
    HtmlPage page = webClient.getPage("/");

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

Lorsque vous exécutez ce test, vous pouvez voir des avertissements ou des erreurs 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 devriez pas modifier), vous pouvez empêcher ces erreurs de faire échouer votre test, en appelantsetThrowExceptionOnScriptError avecfalse:

@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. 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 de votre choix, envoyer et récupérer des données selon vos besoins.

La récupération, l'analyse, le stockage et l'analyse des données à partir de sites Web sont connus sous le nom de raclage Web. HtmlUnit peut vous aider à récupérer et à analyser des pièces.

L'exemple précédent montre comment nous pouvons entrer dans n'importe quel site Web et naviguer à travers celui-ci, en récupérant toutes les informations souhaitées.

Par exemple, allons à l'archive complète des articles de l'exemple, accédez au dernier article et récupérez son titre (première balise<h1>). Pour notre test, cela suffira; mais, si nous voulions stocker plus d'informations, nous pourrions, par exemple, récupérer les en-têtes (toutes les balises<h2>) également, ayant ainsi 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 deuse XPath syntax. HtmlUnit nous permet de l'utiliser, donc nous le ferons.

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

Tout d’abord, remarquez comment - dans ce cas, nous ne sommes pas intéressés par CSS ni par JavaScript et souhaitons simplement analyser la présentation HTML. Nous avons donc désactivé CSS et JavaScript.

Dans un vrai web scraping, vous pourriez prendre par exemple les titresh1 eth2, 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 l'exemple:

image

6. Qu'en est-il AJAX?

Les fonctionnalités AJAX peuvent poser 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 que vous souhaitez. Il y a plusieurs façons de les gérer:

  • Vous pouvez utiliserwebClient.setAjaxController(new NicelyResynchronizingAjaxController()). Cela resynchronise les appels effectués à partir du thread principal et ces appels sont effectués de manière synchrone pour garantir l'existence d'un état stable à tester.

  • Lors de la saisie d'une page d'une application Web, vous pouvez attendre quelques secondes afin de laisser suffisamment de temps pour laisser les appels AJAX se terminer. Pour ce faire, vous pouvez utiliserwebClient.waitForBackgroundJavaScript(MILLIS) ouwebClient.waitForBackgroundJavaScriptStartingBefore(MILLIS). Vous devriez les appeler après avoir récupéré la page, mais avant de l'utiliser.

  • Vous pouvez attendre que certaines conditions attendues liées à l'exécution de l'appel AJAX soient remplies. Par exemple:

for (int i = 0; i < 20; i++) {
    if (condition_to_happen_after_js_execution) {
        break;
    }
    synchronized (page) {
        page.wait(500);
    }
}
  • Au lieu de créer unnew WebClient(), qui utilise par défaut le navigateur Web le mieux pris en charge, essayez d'autres navigateurs car ils pourraient mieux fonctionner avec vos appels JavaScript ou AJAX. Par exemple, cela va créer un client Web qui utilise un navigateur Chrome:

WebClient webClient = new WebClient(BrowserVersion.CHROME);

7. Un exemple avec Spring

Si nous testons notre propre application Spring, les choses deviennent un peu plus faciles -we no longer need a running server.

Implémentons un exemple d'application très simple: juste un contrôleur avec une méthode qui reçoit un texte et une seule page HTML avec un formulaire. L'utilisateur peut entrer un texte dans le formulaire, soumettre le formulaire et le texte sera affiché sous ce formulaire.

Dans ce cas, nous utiliserons un modèleThymeleaf pour cette page HTML (vous pouvez voir un exemple complet de Thymeleafhere):

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

La clé ici est de construire l'objetWebClient en utilisantMockMvcWebClientBuilder à partir desWebApplicationContext. Avec lesWebClient, nous pouvons obtenir la première page de la navigation (remarquez comment elle est servie parlocalhost), et commencer à naviguer à partir de là.

Comme vous pouvez le constater, le test analyse le formulaire et saisit un message (dans un champ avec un identifiant "message"), le soumet et, sur la nouvelle page, affirme que le texte reçu (le champ avec un identifiant "reçu") est le identique au texte que nous avons soumis.

8. Conclusion

HtmlUnit est un excellent outil qui vous permet de tester facilement vos applications Web, en remplissant les champs de formulaire et en les soumettant comme si vous utilisiez Internet sur un navigateur.

Il s'intègre parfaitement à Spring 4 et, avec le cadre de test Spring MVC, 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 (récupération de site Web).

Vous pouvez obtenir le codeover on Github.