justinsilvestre
Unit Testing in ATG

Updated a year ago

Front-End Unit Testing in ATG

ATG does not promote any particular structure for your front-end codebase. If you want automated testing for your front-end code, it's up to you to structure your project in a way that makes testing easy.

This tool I've written can help you keep your JSP code structured in just such a way. Normally, unit testing JSP code would be impossible. This is because your average JSP file is doing two jobs at once:

  • fetching and manipulating data from the back-end
  • visual presentation of that data

This tool lets you use dependency injection and the façade pattern to treat these two thing as separate units. With better separation of concerns, it becomes possible to do unit tests where previously only slow, complex integration tests would have worked.

How it works

All this basically boils down to keeping things in separate files that you would normally have all mixed together. So, where you may be used to writing something like this...

...
<h1>Your Cart</h1>
<section>
<c:choose>
  <c:when test="${order.totalItemCount == 0}">
    <p>Your shopping cart is empty.</p>
  </c:when>
  <c:otherwise>
    <p>You have <strong>${order.totalItemCount}</strong> items in your cart.</p>
  </c:otherwise>
</c:choose>
</section>
...

...now, you'll end up writing two separate chunks. Your presentation layer will look something like this:

<h1>Your Cart</h1>
<section id="cart-details">
  {cartIsEmpty
    ? <p>Your shopping cart is empty.</p>
    : <p>You have <strong>{cartCount}</strong> items in your cart.</p>}
</section>
// ...

...and in a separate file, you'll put all the code for fetching and manipulating back-end stuff in a JavaScript object:

{
  cartIsEmpty: 'order.totalItemCount == 0',
  cartCount: '${order.totalItemCount}'
}

These two components remain separate up until you run your build task.

$ npm run jsxquery-build

The end result is an ordinary JSP file just like you're used to.

<h1>Your Cart</h1>
<section>
<c:choose>
  <c:when test="${order.totalItemCount == 0}">
    <p>Your shopping cart is empty.</p>
  </c:when>
  <c:otherwise>
    <p id="cart-notice">You have <strong>${order.totalItemCount}</strong> items in your cart.</p>
  </c:otherwise>
</c:choose>
</section>

So what's the point?

Since the end result is the same as writing raw JSTL, all the debugging skills you have acquired during your past experience with JSPs are still applicable. But with all back-end logic cleanly separated out from the view logic, some new possibilities open up for you.

Testing with fixtures

Remember: that JavaScript object with your back-end logic doesn't get combined with the presentation layer until that final build step. This means you can easily swap out that object for one containing test data like this:

{
  cartIsEmpty: false,
  cartCount: 3
}

Now, if you try running your build task again, you'll be able to see what would have rendered with that data. You don't even need to have a server running!

<h1>Your Cart</h1>
<section id="cart-details">
  <p id="cart-notice">You have <strong id="cart-count">3</strong> items in your cart.</p>
</section>

If you wanted to test how your page would render if the user had an empty cart, just swap out your test data.

{
  cartIsEmpty: true,
  cartCount: 0
}

Build again, and you'll be able to see the result. Again, this is the kind of thing you could run in your browser without any server involvement in the least.

<h1>Your Cart</h1>
<section id="cart-details">
  <p id="empty-cart-notice">Your shopping cart is currently empty.</p>
</section>

Since the data is stored in an ordinary JavaScript object, you can keep any number of such objects saved to help you test all sorts of different page states. Then, you can see what your JSP page would look like under all sorts of different conditions. You don't need to worry about clicking around and filling out forms over and over if you have the right test data.

With good fixtures, it takes less time to tell whether a bug is a front-end problem, and you can delegate tasks more efficiently when you need to.

Without all the bulk from the back end, you are also free to write automated tests with tools like Jasmine. This opens up the door to TDD, which could mean 40-90% fewer bugs by the end of your project.