Angular.js Unit Test with Custom Service Example
(Updated 7/13/2014)
I've been working a lot with Angular.js lately, and there were a lot of unknowns for me when it came to unit testing my code. Below is an example that I hope will help other people who have questions about how to write unit tests for Angular.
I use Jasmine as the unit testing framework, and in the example below I'm testing controller named DocumentController which makes use of a service called AttachmentService. In the example below, we're testing the ability of the DocmentController to delete attachments, which it does with the help of AttachmentService.
I've commented it heavily in an attempt to be very clear as to what this code is doing. I hope this helps you. :)
I've been working a lot with Angular.js lately, and there were a lot of unknowns for me when it came to unit testing my code. Below is an example that I hope will help other people who have questions about how to write unit tests for Angular.
I use Jasmine as the unit testing framework, and in the example below I'm testing controller named DocumentController which makes use of a service called AttachmentService. In the example below, we're testing the ability of the DocmentController to delete attachments, which it does with the help of AttachmentService.
I've commented it heavily in an attempt to be very clear as to what this code is doing. I hope this helps you. :)
//References to the JS files that are required by these tests and everything involved.
//The order matters!
/// <reference path="../jasmine/jasmine.js" />
/// <reference path="../jasmine/boot.js" />
/// <reference path="../angular.js" />
/// <reference path="../angular-mocks.js" />
/// <reference path="../underscore.js" />
/// <reference path="../ui-bootstrap-0.10.0.js" />
/// <reference path="../ui-bootstrap-tpls-0.10.0.js" />
/// <reference path="../App/app.js" />
/// <reference path="../App/Controllers/DocumentController.js" />
/// <reference path="../App/jquery.fileupload-angular.js" />
//Describe our test "Spec" (Specification)
describe("Ang: DocumentController Tests ->", function () {
var _scope;
var _controller;
//This is equivalent to calling angular.mocks.module() -- NOT angular.module().
//Every time you call angular.module() it recreates the module, so that would be bad here.
//Instead, angular.mocks.module() loads up our module for testing.
//IMPORTANT: We need to call this in a seperate beforeEach() than the one below, or else
//you'll get an error about the injector already having been created and not being able
//to create a new module.
beforeEach(module("myApp"));
beforeEach(inject(function ($injector, $rootScope, $controller) {
var attachmentServiceMock;
//For more than one service, I would put service mocking into seperate method(s)
attachmentServiceMock = {
execute: function (url, method, successCallback, errorCallback) {
successCallback();
}
};
_scope = $rootScope.$new(); //Get a new scope for injection
expect(_scope).not.toBeNull(); //Make sure the new scope isn't null
_scope.model = []; //Init our model
//Instantiate the controller instance we'll be testing with,
//passing in the dependencies
_controller =
$controller('DocumentController',
{
$scope: _scope,
AttachmentService: attachmentServiceMock,
});
//Make sure the controller isn't null
expect(_controller).not.toBeNull();
}));
it("should delete attachment successfully", function () {
//Our fake attachments. The actual values aren't really needed, but help to illustrate
_scope.attachments = [
{ deleteUrl: "http://someurl/delete/blah.jpg/1", deleteType: "DELETE" },
{ deleteUrl: "http://someurl/delete/blah2.jpg/1", deleteType: "DELETE" },
{ deleteUrl: "http://someurl/delete/blah3.jpg/1", deleteType: "DELETE" }
];
//Create a spy on window.confirm() so we can fake the "OK" button click
spyOn(window, 'confirm').and.returnValue(true);
//Call the method we're testing
_scope.deletePreviouslyUploadedAttachment(1);
//Confirm expected result
expect(_scope.attachments.length).toEqual(2);
});
//..other tests...
});
//The order matters!
/// <reference path="../jasmine/jasmine.js" />
/// <reference path="../jasmine/boot.js" />
/// <reference path="../angular.js" />
/// <reference path="../angular-mocks.js" />
/// <reference path="../underscore.js" />
/// <reference path="../ui-bootstrap-0.10.0.js" />
/// <reference path="../ui-bootstrap-tpls-0.10.0.js" />
/// <reference path="../App/app.js" />
/// <reference path="../App/Controllers/DocumentController.js" />
/// <reference path="../App/jquery.fileupload-angular.js" />
//Describe our test "Spec" (Specification)
describe("Ang: DocumentController Tests ->", function () {
var _scope;
var _controller;
//This is equivalent to calling angular.mocks.module() -- NOT angular.module().
//Every time you call angular.module() it recreates the module, so that would be bad here.
//Instead, angular.mocks.module() loads up our module for testing.
//IMPORTANT: We need to call this in a seperate beforeEach() than the one below, or else
//you'll get an error about the injector already having been created and not being able
//to create a new module.
beforeEach(module("myApp"));
beforeEach(inject(function ($injector, $rootScope, $controller) {
var attachmentServiceMock;
//For more than one service, I would put service mocking into seperate method(s)
attachmentServiceMock = {
execute: function (url, method, successCallback, errorCallback) {
successCallback();
}
};
_scope = $rootScope.$new(); //Get a new scope for injection
expect(_scope).not.toBeNull(); //Make sure the new scope isn't null
_scope.model = []; //Init our model
//Instantiate the controller instance we'll be testing with,
//passing in the dependencies
_controller =
$controller('DocumentController',
{
$scope: _scope,
AttachmentService: attachmentServiceMock,
});
//Make sure the controller isn't null
expect(_controller).not.toBeNull();
}));
it("should delete attachment successfully", function () {
//Our fake attachments. The actual values aren't really needed, but help to illustrate
_scope.attachments = [
{ deleteUrl: "http://someurl/delete/blah.jpg/1", deleteType: "DELETE" },
{ deleteUrl: "http://someurl/delete/blah2.jpg/1", deleteType: "DELETE" },
{ deleteUrl: "http://someurl/delete/blah3.jpg/1", deleteType: "DELETE" }
];
//Create a spy on window.confirm() so we can fake the "OK" button click
spyOn(window, 'confirm').and.returnValue(true);
//Call the method we're testing
_scope.deletePreviouslyUploadedAttachment(1);
//Confirm expected result
expect(_scope.attachments.length).toEqual(2);
});
//..other tests...
});
Comments
Post a Comment