Rule Them All meets PhantomJS to test JAX-RS javascript client generation


HTMLUnit is great to test web applications but sadly it doesn’t support recent js libraries (AngularJs/JQuery to not say their names).

PhantomJs is a great alternative but sometimes setup can be boring or too complicated for what you need.

To avoid to spend time on setups I added to “Rule Them All!” project a simple PhantomJs rule. Idea is really to just manage the lifecycle of PhantomJs and to provide a getDriver() method:

public class JQueryClientGeneratorTest {
    @ClassRule
    public static final PhantomJsRule PHANTOM_JS = new PhantomJsRule();

    @Test
    public void doTest() {
        assertTrue(PHANTOM_JS.get("http://....").contains("OK"));
    }

    @Test
    public void angular() {
        final PhantomJSDriver driver = PHANTOM_JS.getDriver();
        driver.get(home);
        driver.executeAsyncScript( // wait for angular to have done its work
                "var callback = arguments[arguments.length - 1];" +
                        "var e1 = document.querySelector('body');" +
                        "if (window.angular) {" +
                        "angular.element(e1).injector().get('$browser').notifyWhenNoOutstandingRequests(callback);" +
                        "} else {callback()}"
        );
        final String pageSource = driver.getPageSource();
        assertTrue(pageSource, pageSource.contains("success"));
    }
}

So basically the setup is pretty simple and then usage is PhantomJSDriver one (can be used as any selenium WebDriver but few specific methods like executeAsyncScript are quite interesting ;)).

Why did I need it?

Writing a javascript client generator for JAX-RS endpoint (https://github.com/rmannibucau/jaxrs-js-client) I needed to test it.

For JAX-RS it was quite trivial: just use OpenEJB ApplicationComposer:

@SimpleLog
@EnableServices("jaxrs")
public class JQueryClientGeneratorTest {
    @Rule
    public final ApplicationComposerRule rule = new ApplicationComposerRule(this);

    @RandomPort("httpejbd")
    private URL httpEjbdPort;

    @Module
    @Classes(innerClassesAsBean = true)
    public WebApp war() {
        return new WebApp();
    }

    @Test
    public void js() {
        // TODO
    }

    @ApplicationPath("api")
    public static class App extends Application {}

    @Path("simple-resource")
    public static class SimpleResource {
        @GET
        public String aGet() {
            return "get";
        }
    }
}

So what do I have with it:

  • A JAX-RS application deployed
  • on a random port
  • (bonus) nice logs (one line)

Now how do I test my JQuery client?

First point was to write a html page…yes but I don’t have a real war…but I have a light servlet support with OpenEJB embedded.

So finally I wrote a servlet serving my html page (I could have copy the content from a file but for the test and the need I made it simple and wrote the html directly in the servlet):

@WebServlet(urlPatterns = "/home")
public class Home extends HttpServlet {
    @Override
    protected void service(final HttpServletRequest req, final HttpServletResponse resp)
                           throws ServletException, IOException {

        resp.getWriter().write(
                "<html>" +
                "<head>" +
                "  <script src=\"jquery.js\"></script>" +
                "  <script src=\"api?jqueryclient=myClient\"></script>" +
                "</head>" +
                "<body>" +
                "  <div id=\"content\"></div>" +
                "  <script>" +
                "    $(function () {" +
                "      myClient.SimpleResource.aGet()" +
                "        .done(function (d) {" +
                "           $('#content').html(d);" +
                "        });"
                "    });" +
                "  </script>" +
                "</body>" +
                "</html>"
        );
    }
}

This html page needs 2 scripts:

  • api?jqueryclient=myClient: this one is easy: generated by the lib so nothing to do
  • jquery.js: here we’ll use the same hack as for the html page but reading content from a file
@WebServlet(urlPatterns = "/jquery.js")
public class JQuery extends HttpServlet {
    @Override
    protected void service(final HttpServletRequest req, final HttpServletResponse resp)
                           throws ServletException, IOException {

        resp.setContentType("application/javascript");
        final ClassLoader loader = Thread.currentThread().getContextClassLoader();
        final String jquery = IO.slurp(loader.getResourceAsStream("jquery-2.1.1.js"));
        resp.getWriter().write(jquery);
    }
}

Now all is setup and running the program as a main works…but how to test it?

I started to test using HtmlUnit…but it doesn’t parse jquery correctly. So finally I fallback on PhantomJs and wrote the rule from Rule Them All!

Finally the test looks like:

@SimpleLog
@EnableServices("jaxrs")
public class JQueryClientGeneratorTest {
    @ClassRule
    public static final PhantomJsRule PHANTOM_JS = new PhantomJsRule();

    @Rule
    public final ApplicationComposerRule rule = new ApplicationComposerRule(this);

    @RandomPort("httpejbd")
    private URL httpEjbdPort;

    @Module
    @Classes(innerClassesAsBean = true)
    public WebApp war() {
        return new WebApp();
    }

    @Test
    public void generation() throws Exception {
        assertTrue(PHANTOM_JS.get(httpEjbdPort.toExternalForm() + "openejb/home").contains("get"));
    }

    @ApplicationPath("api")
    public static class App extends Application {}

    @Path("simple-resource")
    public static class SimpleResource {
        @GET
        public String aGet() {
            return "get";
        }
    }

    @WebServlet(urlPatterns = "/home")
    public static class Home extends HttpServlet {
        @Override
        protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
            resp.getWriter().write(
                    "<html>" +
                    "<head>" +
                            "<script src=\"jquery.js\"></script>" +
                            "<script src=\"api?jqueryclient=myClient\"></script>" +
                    "</head>" +
                    "<body>" +
                    "<div id=\"content\"></div>" +
                    "<script>" +
                            "$(function () {" +
                            "myClient.SimpleResource.aGet()" +
                            ".done(function (d) {" +
                            "$('#content').html(d);" +
                            "});});" +
                    "</script>" +
                    "</body>" +
                    "</html>"
            );
        }
    }

    @WebServlet(urlPatterns = "/jquery.js")
    public static class JQuery extends HttpServlet {
        @Override
        protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
            resp.setContentType("application/javascript");
            final String jquery = IO.slurp(Thread.currentThread().getContextClassLoader().getResourceAsStream("jquery-2.1.1.js"));
            resp.getWriter().write(jquery);
        }
    }
}

Nothing very complicated, it not as slow as starting a complete server (in particular when multiplying tests) and it is easily debuggable :).

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s