Thursday, August 5, 2010

Testing rich webapps from JUnit with Rhino and Envjs

Envjs is an implementation of the DOM in Javascript. Together with Rhino, it can function as a headless web browser in Java. Picture HtmlUnit, but with flawless Javascript support. For instance, the latest version of JQuery works flawlessly in Envjs on Rhino but won't even load in HtmlUnit.

Here's a simple way to tie them together in JUnit. Wonderful testing frameworks to follow (looking at you, Mike ;)

Not too many Groovy-isms here, can easily be converted to plain Java.

package com.cadrlife.mywebapp;
import groovy.util.GroovyScriptEngine;
import org.apache.commons.lang.RandomStringUtils;
import org.junit.Before;
import org.junit.Test;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.google.common.io.Resources;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.ScriptableObject;
import static org.junit.Assert.*
public class MyWebAppTest {
private Context cx;
private ScriptableObject scope;
@Before
public void setup() throws Exception {
cx = ContextFactory.getGlobal().enterContext();
cx.setOptimizationLevel(-1);
cx.setLanguageVersion(Context.VERSION_1_5);
scope = cx.initStandardObjects();
// Assumes we have env.rhino.js as a resource on the classpath.
String envjs = getClass().getResourceAsStream("env.rhino.js").text;
String printFunction = "function print(message) {java.lang.System.out.println(message);}";
cx.evaluateString(scope, printFunction, "print", 1, null);
cx.evaluateString(scope, envjs, "env.rhino.js", 1, null);
// This will load the home page DOM.
run("window.location='http://localhost:5000'");
// Whatever script JS includes the home page has.
def libs = ["/js/jquery-1.4.2.min.js",
"/js/jquery-ui-1.8.2.custom.min.js",
"/js/app.js"];
for (String lib : libs) {
run(new URL("http://localhost:5000" + lib).text);
}
// Whatever happens on document ready.
run('appInit()');
}
@Test
public void navigation() throws Exception {
def h1Text = '$("h1:first").text()';
run('$(".nav .homeLink").click()');
assertJsEquals('Welcome', h1Text);
run('$(".nav .dashboardLink").click()');
assertJsEquals('Dashboard', h1Text);
}
private void assertJsEquals(String a, String b) {
assertEquals(a,run(b));
}
private void assertJsContains(String a, String b) {
String result = run(b);
assertTrue("${a} does not contain ${a}", result.contains(a));
}
private String run(String js) throws Exception {
Object result = cx.evaluateString(scope, js, "run", 1, null);
return Context.toString(result);
}
}

No comments: