One of the many bits of magic AngularJS does for you is automatic promise resolution so that you can write code like this:
function SomeCtrl($scope, Resource) {
$scope.items = Resource.query();
}
However, the service I'm working with functions via JSON-RPC and ngResource doesn't seem to play nicely with it. I've had to mock out the backend to respond appropriately, but for some reason I cannot seem to figure out how to test the response at the controller level.
"use strict";
describe('controllers', function() {
describe('LibraryCtrl', function() {
var scope, ctrl;
beforeEach(inject(function($rootScope, $controller) {
JsonRpc.respondWith({ some : data });
scope = $rootScope.$new();
ctrl = $controller(LibraryCtrl, { $scope : scope });
// controller does `$scope.items = RPCService.get()`
}));
it("should create a `items` model with data fetched via JSON-RPC", function() {
// here's where I'm very confused
// JsonRpc does $httpBackend.flush()
JsonRpc.respond();
// now I want to inspect the property on the controller
expect(scope.items).toEqualData( { some : data } );
});
});
});
The problem is, scope.items is a promise. That particular property will remain a promise, because apparently $digest() doesn't resolve the promise and re-attach the return value as the model value. I can't do a simple equality assertion.
My workaround is to use a promise capture mock:
var result = {
capture : function(v) { result.value = v; },
value : null,
};
and then try this:
scope.items.then(result.capture);
expect(result.value).toEqualData(...);
This does work (but feels somehow wrong). As a sidenote (for brownie points, this isn't the actual question), swapping the result.capture chain with JsonRpc.respond() causes the test to fail (with result.value being null).
Here are the questions: what is the correct way to test promises set as scope models? Is this result.capture thing I'm doing okay, or is there a better way to go about it? Also, I'd really like to know why reversing the promise attachment with the response flushing causes the response to not resolve and capture...?
Good question. My first reaction is it seems your controller unit test is actually testing ngResource here. In my controller tests I mock out the resource:
mockResource = {query: jasmine.createSpy('query').andReturn([{id: 1, name: foo}])
module(function ($provide) {
$provide.value('Resource', mockResource);
...
expect(mockResource.find).toHaveBeenCalled()
expect(scope.items)....
This allows you to test the controller in isolation.