Unit Testing Ember Controllers backed by Ember Data
I’ve been trying to take deeper and deeper dives into Ember and Ember Data over the last year. We rewrote the LayerVault file page in Ember. Progress in the Ember, Ember Data, and ember-cli communities continues at a breakneck pace. One of the issues that I’ve run into regularly, however, is to have great unit tests for controllers when Ember Data is in play.
Ember Data is great, but there are many things going on underneath the hood. Stubbing methods on models that do not yet exist is difficult.
Integration or acceptance tests are inappropriate for complex methods with many side effects: I write a unit for each side effect. While Pretender and ic-ajax are great for high-level tests, I often find myself craving specific unit tests for controller-level peace of mind.
Before continuing, let’s talk about current setup. The approaches to be layed out in this blog post will morph and change as versions get bumped and the cruel march of time marches on:
- ember-cli v0.1.15
- Ember v1.10.0
- Ember Data v.1.0.0-beta-15
- JSON API pre-1.0
I’m a bit spoiled coming from the land of RSpec. let
, before(:each)
, and shared examples have
allowed tests to be as DRY as the production code itself. Going from RSpec to QUnit is cumbersome,
but many of the same concepts can be achieved.
Please Note: I am not claiming that my methods are the best way of accomplishing the task at hand. If there is a lone soul out there that has a better approach, or a polite smackdown, please do get in touch.
Setting up tests to use DS.Store
I’ve found the following pattern to be most effective for setting up unit tests that need to use the store.
Our initial setup is straightforward. Initialize a store
and an App
.
Testing that a model gets saved
I often find it useful to ensure that the DS.Model#save
method is being called
when I trigger an action. This helps keep my green phase well-defined before moving onto the refactor
phase. It’s important to ensure that a model is saved, especially when a single action might
chain together several promises.
To make sure that save gets called, I override and short-circuit the scheduleSave
which does most
of the heavy lifting within a DS.Store
instance.
Testing that an ajax method is triggered
In a few of my applications, I find myself manually managing the AJAX calls to persist relationships. The reason for this is two-fold: the JSON API spec is changing quickly with no solid Ember Data adapter, and Ember Data’s relationship management is still in its nascency when it comes to persisting relationships.
Instead, I instantiate each relationship-related AJAX request at a lower-level by calling methods directly on an adapter instance. This has the added benefit of making things slightly more testable.
Let’s dive in.
Let’s take a look at the example source, where we are adding a Tag object to a Post
using the "addTag"
action:
Next, let’s take a look at the test for the AJAX request:
There’s plenty going on here, with only one assertion: Did we make the request we were expecting to make? The reason for structuring the test this way (i.e. using a flag instead of direct assertions), is this allows other AJAX calls to be added to the action without breaking this test.
Although this test requires plenty of setup, it serves well to make sure regressions are not introduced.
Is there a better way?
So that’s a quick overview of how I write tests given the current state of JSON API and Ember Data. If you have any recommendations, I would love to hear them.