When I started on my first Angular+Rails project around 12 months ago, there wasn't a lot of guidance around code organization, interop, and testing, and we got a lot of these things wrong. Since then, I've worked on several additional projects using the same tech stack and have had several more chances to screw it up all over again. After a few of these, I feel like I've finally got some conventions in place that work well for better code organization, interop, and testing.
This morning the team over at Localytics (hi Raj!) wrote up a good retrospective on their use of Angular + Rails over the past year, including lessons they learned and ongoing challenges. They touch on several of the same issues that my colleagues and I have run into, and the writeup inspired me to dust off my old busted blog to document some of my own findings.
One area that I felt like needed some further clarity was testing. In particular, how a Rails-centric application can cleanly and easily integrate tests around Angular frontend logic. Fortunately, once you figure out how to set this up, you'll find that unit testing Angular code in Jasmine -- especially controller and factory code -- is surprisingly easy to do. It's really the first time I've been sufficiently happy with a frontend testing configuration.
To see a working example for yourself and hack around with it, go snag the
I pushed up to GitHub. Bundle and run it, and play around with the shockingly
awesome todo list application. Because the world really needed another one of
those. When you've had enough of that, take a look at the contents of the
We're using the jasmine-rails test
runner with CoffeeScript here, because that's what works for me (sorry Karma).
Pay close attention to the
spec_helper.coffee, which does much of
the dependency injection needed to provide clean and intuitively named
interfaces in our example controller spec.
This gives us nice ways to interface with the factories and controllers we're defining, as well as Angular's own ngMock library (super useful for stubbing server-side endpoints), the event loop, and even template compilation for partials and directives. A couple of these are illustrated in the sample controller spec shown here:
Jasmine's syntax should be very familiar to anyone who does RSpec BDD work, and
the work we've done in our spec helper really cleans up the beforeEach setup
that's required in each individual controller spec. These particular tests make
heavy use of ngMock, which you won't always need to use, and the calls to
flush() are required to fulfill pending requests, preserving the
async nature of the backend but allowing the tests to execute synchronously.
Testing Continuously With Guard
Although the Jasmine web interface is nice, but I'm a big fan of using Guard in order to watch for filesystem events and kick off automated test runs from the command line. By including the guard-jasmine gem and updating our Guardfile we can continuously test both our server-side RSpec logic and the Jasmine unit tests all at the same time through a single interface:
One thing I haven't addressed here is directive testing, which can be a bit more difficult. I'll try to address that in a future post, or if you have your own recipes, feel free to link em up in the comments.
Special thanks to Mark Bates for working with me on early versions of this approach, and convincing me that Angular was worth looking at in the first place.