15

I'm developing a Java 6 EE application and I'm testing my jsp code with another one with a testing version of the function calls and code used in the original one but it seems loose and impractical. Is there a good way to accomplish this kind of testing?

zamancer
  • 151
  • 1
  • 1
  • 3

5 Answers5

17

If you haven't read about MVC (model view controller), do so. You shouldn't have code in a JSP, just display. Putting code in JSP is very 1900's.

Seriously though, if there is no code in the JSP, you aren't testing the JSP. You are testing the action/flow. Then you could use HttpUnit or Selenium. The big difference is that Selenium tests from a real browser.

Jeanne Boyarsky
  • 1,746
  • 14
  • 21
  • 1
    Obviously, the business logic should not be found in JSP, agreed on that. But this doesn't mean JSP is forbidde of the UI behavioral logic. Conditionally enabling/disabling input fields, buttons, etc - all this is a normal part of JSP and for best results it should be unit-tested too. – JBM Nov 30 '20 at 12:52
15

I don't think there is a good way to test JSPs, mainly because they were developed before unit testing became a focus of development.

Robert Martin wrote an article several years ago about hacking the JSP compiler so that you can direct, non-container based unit tests. His idea was good, but it was broken with the very next TomCat major release. There's just too much magic going on.

I disagree with the "just don't add code and you won't need to test it" idea. OBVIOUSLY you should not be putting code in the JSP. But nonetheless a complex UI will often have display logic that could be profitably unit tested.

Consider this example:

<c:choose>
  <c:when test="${mydto.showAdminMenu}">
   The admin menu....
  </c:when>
  <c:otherwise>
    Something completely different
  </c:otherwise>
</c:choose>

This code is already well factored: the logic to decide whether we show the admin menu is not in the view. Nonetheless, if there were an easy way to unit test JSPs, then we could write a test to show that the behavior we want actually appears, and it would protect us from a change to the page that accidentally made the admin menu visible when it shouldn't be.

portabella
  • 151
  • 1
  • 2
4

There exists a program (used by whatever application server you are using) that compiles a .jsp file into a .java file. For example, the sun/oracle version jspc.

Once you have the .java that would be produced by the .jsp translation (you might even want to consider using this as part of the build process - precompiling the jsp for performance improvements on the first hit), you can then run tests against it by mocking the request and verifying the response is what you are expecting.

(edit with example:)

The key method for this is the _jspService(HttpServletRequest, HttpServletResponse) method.

A trivial hello world jsp:

<html>
    <head>
        <title>Hello world</title>
    </head>
    <body>
        <h1>Hello world</h1>
        Today is: <%= new java.util.Date().toString() %>
    </body>
</html>

(test.jsp located within a directory named 'webapp' and also an 'out' directory) When compiled with the command jspc -v -d out -compile -uriroot webapp/ test.jsp places into an out directory a file called test_jsp.java. This file has within it (along with a fair bit of other configuration setup):

  public void _jspService(HttpServletRequest request, HttpServletResponse response)
        throws java.io.IOException, ServletException {

    PageContext pageContext = null;
    HttpSession session = null;
    ServletContext application = null;
    ServletConfig config = null;
    JspWriter out = null;
    Object page = this;
    JspWriter _jspx_out = null;
    PageContext _jspx_page_context = null;

    try {
      response.setContentType("text/html");
      pageContext = _jspxFactory.getPageContext(this, request, response,
                null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;

      out.write("<html>\n\t<head>\n\t\t<title>Hello world</title>\n\t</head>\n\t<body>\n\t
\t<h1>Hello world</h1>\n\t\tToday is: ");
      out.print( new java.util.Date().toString() );
      out.write("\n\t</body>\n</html>\n\n");
    } catch (Throwable t) {
      if (!(t instanceof SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try { out.clearBuffer(); } catch (java.io.IOException e) {}
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}

At this point, its a check to make sure that the JspWriter is called with write or print and the contents of the call are what you expect it to be.

All that said, in an ideal world, one shouldn't have any logic within the jsp - such logic would either be in the controller or taglibs which are tested with other techniques.

  • 1
    What would be in the JSP that would be tested/mocked out? Setting data in the request/session and then checking the display? Or is this if good practice isn't followed and there is actual logic in the JSP? – Jeanne Boyarsky Jun 06 '12 at 01:58
  • @JeanneBoyarsky Updated with example jsp and code. I wouldn't consider it to be good practice to try testing this with a traditional junit - its another realm of testing. The depth of mocking could be awkward depending on the tool set (e.g. subclassing JspWriter so that one can easily test what is sent to it). –  Jun 06 '12 at 16:03
3

You can also consider using a HTTP unit test framework like HTTPUnit | http://httpunit.sourceforge.net/.

Another important point is to well separate concerns of your application.

E.g. using techniques like TDD (http://en.wikipedia.org/wiki/Test-driven_development), you'll design types for testability.

Types consumed within JSP will be tested in specific unit tests. If this is not possible, you should simulate user -> browser interaction (again, HTTPUnit o similar tool).

gsscoder
  • 246
  • 1
  • 4
2
  • try to externalize the functional code of the servlet to test it outside of a servlet context with real unit tests
  • test the servlet endpoints with tools like:
    • HTTPUnit
    • HtmlUnit
    • Selenium
    • Cactus
    • JspTest
    • ...
haylem
  • 28,856
  • 10
  • 103
  • 119