Content

Introduction

A frequent question we get is "how do I test my JavaScript?". There is nothing really specific for using JavaScript, it is automatically processed. So, you just need to .getPage(), find the element to click(), and then check the result. Tests for complex JavaScript libraries are included in HtmlUnit test base, you can find it here which is useful to get an idea.

Usually, you should wait a little, as HtmlUnit can finish before the AJAX response is retrieved from the server, please read this FAQ.

Enable/Disable JavaScript support

The JavaScript support is enabled per default like in real browsers.

Handling of JavaScript errors

But: There is one major difference compared to real browsers - in the default configuration HtmlUnit will stop JavaScript execution if an unhandled JavaScript error occurs. This is done because HtmlUnit was initially designed for testing where fail early is a good practice.

You can change this to silently (HtmlUnit will still log the exceptions) ignore the JavaScript exceptions (like in real browsers) by setting the option throwExceptionOnScriptError to false.

 final WebClient webClient = new WebClient();
 webClient.getOptions.setThrowExceptionOnScriptError(false);

Completely disable the JavaScript support

For some use cases it might be helpful to completely disable the JavaScript support (e.g. for better performance and less memory usage). HtmlUnit offers two way to do this; you can completely disable JavaScript or only temporary.

To disable the JavaScript support (without the possibility to enable it for the WebClient later on) a special WebClient constructor is available. This is the most performance/resource effective solution.

 WebClient webClient = new WebClient(BrowserVersion.FIREFOX, false, null, -1);

Temporary disable the JavaScript support

By using the WebClientOptions it is possible to disable JavaScript support for a specific client at any time and enable it again later. However, this only disables the execution of the script tags and the triggering of event listeners. However, the JavaScript objects will still be created.

 final WebClient webClient = new WebClient(BrowserVersion.FIREFOX);
 webClient.getOptions.setJavaScriptEnabled(false);
 ....
 webClient.getOptions.setJavaScriptEnabled(true);

If you like to check the state of the JavaScript support from your code it is recommended to use WebClient#isJavaScriptEnabled() because this checks both ways of disabling Javascript.

Example: using Document.write()

Lets say that we have a page containing JavaScript that will dynamically write content to the page. The following html will dynamically generate five textfields and place them inside a table. Each textfield will have a unique name created by appending the index to the string "textfield".

<html><head><title>Table sample</title></head><body>
    <form action='/foo' name='form1'>
    <table id="table1">
        <script type="text/javascript">
            for (i = 1; i <= 5; i++) {
                document.write("<tr><td>" + i
                    + "</td><td><input name='textfield" + i
                    + "' type='text'></td></tr>");
            }
        </script>
    </table></form>
</body></html>

We would likely want to test that the five text fields were created, so we could start with this.

@Test
public void documentWrite() throws Exception {
    final WebClient webClient = new WebClient();

    final HtmlPage page = webClient.getPage("http://myserver/test.html");
    final HtmlForm form = page.getFormByName("form1");
    for (int i = 1; i <= 5; i++) {
        final String expectedName = "textfield" + i;
        Assert.assertEquals(
            "text", 
            form.<HtmlInput>getInputByName(expectedName).getTypeAttribute());
    }
}

We might also want to check off-by-one errors by ensuring that it didn't create "textfield0" or "textfield6". Trying to get an element that doesn't exist will cause an exception to be thrown, so we could add this to the end of the previous test.

try {
    form.getInputByName("textfield0");
    fail("Expected an ElementNotFoundException");
}
catch (final ElementNotFoundException e) {
    // Expected path
}

try {
    form.getInputByName("textfield6");
    fail("Expected an ElementNotFoundException");
}
catch (final ElementNotFoundException e) {
    // Expected path
}

Example: Watching for 'alerts'

Often you want to watch alerts triggered by JavaScript.

<html><head><title>Alert sample</title></head>
<body onload='alert("foo");'>
</body></html>

Alerts are tracked by an AlertHandler which will be called whenever the JavaScript alert() function is called. In the following test, we register an alert handler which just saves all messages into a list. When the page load is complete, we compare that list of collected alerts with another list of expected alerts to ensure they are the same.

@Test
public void alerts() throws Exception {
    final WebClient webClient = new WebClient();

    final List collectedAlerts = new ArrayList();
    webClient.setAlertHandler(new CollectingAlertHandler(collectedAlerts));

    // Since we aren't actually manipulating the page, we don't assign
    // it to a variable - it's enough to know that it loaded.
    webClient.getPage("http://tciludev01/test.html");

    final List expectedAlerts = Collections.singletonList("foo");
    Assert.assertEquals(expectedAlerts, collectedAlerts);
}

Prompts, Confirms and Status line messages

Handling prompt dialogs, confirm dialogs and status line messages work in the same way as alerts. You register a handler of the appropriate type, and it will get notified when that method is called. See WebClient.setPromptHandler(), WebClient.setConfirmHandler() and WebClient.setStatusHandler() for details on these.

Event handlers

Most event handlers are already implemented: onload, onclick, ondblclick, onmouseup, onsubmit, onreadystatechange, ... They will be triggered at the appropriate time just like in a "real browser".

If the event that you wish to test is not yet supported then you can directly invoke it through the ScriptEngine. Note that while the script engine is publicly accessible, we do not recommend using it directly unless you have no other choice. It is much better to manipulate the page as a user would by clicking on elements and shifting the focus around.

Run javascript inside the page/window

Sometimes it is helpful to execute javascript inside the current page e.g. to get the value of window properties or forcing some javascript functions to run. In general, you can do everything you can do with a script tag inside the page and additionally get back the result of the invocation.

String script = "window.name;";
String result = (String) page.executeJavaScript(script).getJavaScriptResult();
}