Whenever I write JavaScript I throw all software craftsmanship discipline out the window. There are no unit tests and very little structure to the code. It's just me and the browser refresh button until we get it right. If you are lucky you may have jQuery to provide a nice CSS selector based api that somewhat shields you from browser quirks. Maybe you even kept your project from devolving to the point where every page is basically its own JavaScript app with no hope of reuse between them. Even then JavaScript is still likely a second class language in your project. Surprisingly, this state of affairs is normal. I want to take my client side scripting just as seriously as any other code in my Grails web app. I want tests, structure, and less boilerplate, and I am going to use AngularJS and CoffeeScript to do that.
I started with a new Grails 2.1 app and implemented the excellent AngularJS tutorial on top of it. Once I got it working I turned all the JavaScript code into CoffeeScript. Overall these technologies work very nicely together. Of course there were a few tricks I learned along the way and I will point those out. Grails and AngularJS are both MVC web frameworks that make heavy use of dependency injection and have great support for testing. There is some overlap that I will discuss later, but for the most part Grails stays on the server side and AngularJS stays on the client side. AngularJS actually feels like a more Grailsy way to handle the view layer than what Grails provides out of the box.
Grails
The Grails side of this is pretty standard. I've used the handy resources plugin to manage my client side static resources like images, js, and css files. It allows you to define dependencies between these resources and then bring the required ones into a page just by specifying the top level of the hierarchy. There is a Grails Controller (shown below) with two kinds of actions. There are pass through actions (list and show) that defer to AngularJS, and end points for restful calls (query). PhoneService is a Grails service that handles the actual data. In a real application it would be backed by a proper data store, but here I hardcoded the actual JSON from the AngularJS tutorial.
AngularJS
Though there is an AngularJS plugin for Grails, I didn't see any reason to use it. The Grails resources plugin makes using AngularJS so simple, and I like having direct control over which version of AngularJS I use and where the files are put.
Much like Grails, AngularJS has its own set of controllers (for page manipulation), services (for general purpose tasks), and dependency injection to tie everything together. The DI feels a lot like Groovy with the way that injection takes place by name automatically. If you look deeper it starts to look a lot like Guice. Below are the Angular controllers used in this application to get JSON data and binding it to the page via $scope.
CoffeeScript
There is much more to say about AngularJS, but before I do I'd like to mention that the AngularJS code that I am showing has been converted to CoffeeScript. I find CoffeeScript to be much more pleasant to use that JavaScript (and more Groovy like). The conversion was relatively straight forward, but there were a few gotchas around scoping of variables due to CoffeeScript's lexical scopes and implicit function safety wrapper. As you can see in the above example it is necessary to prefix controllers with an @ to elevate their scope. Also my AngularJS service test shown below uses a 'setup' variable to give the tests access to objects created in the 'beforeEach' section.
I used the Grails CoffeScript plugin for this, but it was an older version that I found useful. There is a handy way that one can configure arbitrary CoffeeScript files to be converted to JavaScript. I liked this approach because I was content to let the same mechanism convert my CoffeeScript tests that is converting the rest of my CoffeeScript code. The newer version of the plugin doesn't seem capable of this. Being new to CoffeeScript, I also appreciated seeing the generated JavaScript.
Grails and AngularJS Together
There are limitless ways that you could combine Grails and AngularJS. By posting this I hope to gain some new insights. I favor Grails quite a bit here just because I am more comfortable with Groovy. Think of this example as a Grails app which delegates to an AngularJS sidecar specializing in putting the final touches on the view layer.
Grails vs AngularJS responsibilities
Grails | AngularJS |
---|---|
URL mapping for full page transitions | client side data binding and dom manipulation (single page changes) |
provides RESTful services | makes the AJAX calls |
application concerns (overall file layout, server and client side dependencies, building and packaging, control center for all commands) | JavaScript organization and separation of concerns |
server side DI with Spring | client side AngularJS DI |
server side unit, integration and overall functional tests with Spock | JavaScript unit testing with Jasmine |
Rather than use AngularJS routing, I let Grails handle it (mostly because I am more familiar with Grails). You can see in PhoneController above that the pass through actions act very much like AngularJS routes. They map a url to a template (by Grails convention) and an AngularJS controller (by passing an explicit value to the template). In this case the template is a Grails .gsp.
Grails and AngularJS share the templates which in this case are .gsp files. There are two passes to transform these files now. First Grails compiles the .gsp on the server and applies tag libraries, then AngularJS applies it's bindings and directives on the client. This is where AngularJS shines and I let it do most of the work. One thing to remember is that since Grails encodes the output of tag libraries, you can't use the AngularJS binding inside them. For example...
Getting Grails and AngularJS to communicate effectively is simple once you realize that Grails params and AngularJS $scope are both the state that the respective controllers care about. So I found a way to stamp the Grails controller's params onto the AngularJS controller's $scope using ng-init. The main.gsp is a layout that I apply to all my Grails .gsp files. It is basically a place to put common elements that will appear on every page. You can see how I am taking this opportunity to always set the ng-controller (which was specified by name in the Grails controller) and pass the Grails params into it with ng-init.
AngularJS provides resources for making rest calls. This is a great way to communicate with the Grails backend. To remove a lot of boilerplate code I created a service that constructs resources based on Grails URL conventions. You can see in the AngularJS controllers above where I make use of the service named 'Grails' to get phone data. The code for this service is below. It simply substitutes Grails controller, action and id values for their placeholder in the url. It will default to values found in $scope (which were copied from the Grails params if you remember).
Unit Tests
Grails itself offers great support for writing tests, especially with the 2.0+ enhancements that allow better Spock integration. I must say that testing my code with Spock is bliss with its concise syntax, built in mocking, and intuitive assertions.
The thing about AngularJS that really got my attention is the promise that I can be in a similarly happy place when writing JavaScript tests. And it delivers! This is no doubt because there are test gurus behind AngularJS. Jasmine allows my unit tests to have a familiar structure coming from Spock. The DI that is core to AngularJS allows me to isolate my logic and mock what I need to. The tests are run by js-test-driver which needs to connect to a browser. Your web app doesn't have to be running, js-test-driver just uses the browser as an engine to execute the javascript (and you can execute in multiple browsers in parallel). There is an IntelliJ plugin for js-test-driver that lets me run my tests as if they are JUnit. There is even JUnit output that I can use in Jenkins. And surprisingly code coverage for JavaScript (but I lost this when I converted to CoffeeScript). It was trivial to create a unit test for my AngularJS service described above that makes rest calls to Grails. I merely instantiate the service, mock the backend, and assert that the right calls would be made based on the scope and overrides.
Functional Tests
Functional tests run against your app externally, usually through some type of browser automation. AngularJS provides end-to-end testing to accomplish this. These tests are described with Jasmine just like AngularJS unit tests. They are executed in the browser and are blazingly fast. AngularJS e2e tests are awesome, but I used a Grails based solution instead.
Geb is a Groovy way to automate a browser and it works well with Spock to write Grails functional tests. It's basically a Groovy DSL on top of Selenium/WebDriver. Even though it is not as fast as AngularJS e2e tests, I am more familiar with these tools and I like the page object pattern that Geb uses to separate browser automation in the test from the specific markup of the page.
Above you can see the definition of a page. Most of the CSS selectors that address page elements can go here. Aside from avoiding repetition of these selectors, it makes the tests less brittle. When there are changes to the markup you just make a corresponding change to your page definition and all your tests still pass. There's no need to change the test itself unless the behavior of your test conceptually changes.
Below you can see the related test. The setup method navigates to the specific page and the tests use the page object definitions to manipulate and make assertions about the page. Note how I am able to pass 'phoneThumb' an index so that I can click arbitrary thumbnail images. I think this makes for very readable and robust tests. Converting the AngularJS e2e tests from the tutorial to Geb was very intuitive.
Running the Geb tests was not so straight forward. There is a Geb plugin for Grails that does some basic setup of Geb inside of Grails, and there is tighter integration to be had by following this tip from the Geb author. The plugin allows you to run the tests from the Grails command line, but I had to resort to a bit of a hack to run the tests from my IDE. Basically I needed a way to configure Geb outside of the Grails app.
Speaking of Geb configuration, I have a few observations about my choice of driver. I tried HtmlUnitDriver, FirefoxDriver and ChromeDriver. HtmlUnitDriver just didn't work for my tests. FirefoxDriver worked out of the box, but was slow. ChromeDriver was fast (much closer to AngularJS e2e speeds) but there is a minimal amount of setup to be done on your machine first.
Benefits
AngularJS brings a lot of the same good things to my client code that Grails brings to my server side code (structure, DI, tests). CoffeeScript makes my client code look and feel more like Groovy (less boilerplate, many common idioms). Overall my client side scripting is now a first class citizen and sits right beside the rest of my code (literally and figuratively).
Code
All the code is on Gtihub. Feel free to use this as a starting point and send pull requests when you figure out improvements.
Todo
- Run in jenkins build on headless server and capture test failures (bonus points for code coverage)
- Use Testacular rather than js-test-driver to run unit tests.
- Replace CSS and HTML with Less and Jade respectively.
- Full CRUD with RESTful Grails endpoints.