You wish to unit test your controller and service consuming a JSONP API.
Let’s have a look again at our example we wish to test:
var app = angular.module("MyApp", ["ngResource"]);
app.factory("TwitterAPI", function($resource) {
return $resource("http://search.twitter.com/search.json",
{ callback: "JSON_CALLBACK" },
{ get: { method: "JSONP" }});
});
app.controller("MyCtrl", function($scope, TwitterAPI) {
$scope.search = function() {
$scope.searchResult = TwitterAPI.get({ q: $scope.searchTerm });
};
});
Note that it slightly changed from the previous recipe as the TwitterAPI
is pulled out of the controller and resides in its own service now.
Use the angular-seed project and the $http_backend mocking service.
describe('MyCtrl', function(){
var scope, ctrl, httpBackend;
beforeEach(module("MyApp"));
beforeEach(
inject(
function($controller, $rootScope, TwitterAPI, $httpBackend) {
httpBackend = $httpBackend;
scope = $rootScope.$new();
ctrl = $controller("MyCtrl", {
$scope: scope, TwitterAPI: TwitterAPI });
var mockData = { key: "test" };
var url = "http://search.twitter.com/search.json?" +
"callback=JSON_CALLBACK&q=angularjs";
httpBackend.whenJSONP(url).respond(mockData);
}
)
);
it('should set searchResult on successful search', function() {
scope.searchTerm = "angularjs";
scope.search();
httpBackend.flush();
expect(scope.searchResult.key).toBe("test");
});
});
You can find the complete example on github.
Since we now have a clear separation between the service and the controller, we can simply inject the TwitterAPI
into our beforeEach
function.
Mocking with the $httpBackend
is done as a last step in beforeEach
. When a JSONP request happens we respond with mockData
. After the search()
is triggered we flush()
the httpBackend
in order to return our mockData
.
Have a look at the ngMock.$httpBackend module for more details.