Testing should always be a major part of any development project. Angular has been built from the beginning to be testable. It has clear separation of concerns; for example, you do not need to build a full DOM to test a controller. Angular also uses dependency injection everywhere, which makes mocking up objects very easy.
Jasmine and Karma are two tools that allow you to quickly and easily test your Angular code.
This is the actual unit testing library that we will use. Jasmine is a behavior-driven testing framework and is really easy to write.
Karma is the test runner that will watch your files and automatically kick off your tests. It runs on Node.js, so you must have it installed. You can then install Karma with npm
:
install karma-jasmine
Karma can watch your test files and rerun them whenever any of the files change. You can also debug tests in the browser if there are any issues. It is a great complement to Jasmine, and Google recommends both for testing.
The ngMock
handler is used to help mock up your application when testing. When testing, you will not want to create an entire DOM to load just one module. This is where ngMock
can create a controller instance for use, and we can then test it.
This gets the Angular inject services:
inject(toBeInjected)
Use inject to get access to built-in Angular services, such as $controller
and $compile
, or use it to get access to a loading module's services.
Inject can load dependencies if they are wrapped with underscores. For example, inject would load $compile
if it is used as _$compile_
. This is done because in a test, we will need to create a reference to the $compile
service, and most likely, we would want to use $compile
as the variable name. The underscores allow you to inject it and use the $compile
variable.
This can create a mock response:
$httpBackend.when(requestType, url, [requestParameters], [headers]) $httpBackend.expect(requestType, url, [requestParameters], [headers]) $httpBackend.respond(response)
Making AJAX calls is a perfect example of something that should not be done in a unit test. There are too many things out of your control for unit testing. The $httpBackend
handler is provided by ngMock
so that you can create a mock response.
The handler must match the expected method and URL at the very least. You can also match the optional parameters and headers if you plan on making specific requests with them.
When the request is matched, you can send back a string or object as the response. This allows you to create a test that uses $httpBackend
, as you know what the response is going to be.
The difference between
expect
and when
is that expect
has to be called in the test, whereas when
does not have that requirement.
Here is an example of a simple unit test for a controller. First, you must create a controller and then load it in our test:
var firstModule = angular.module('firstModule', ['ngMock']); firstModule.controller('SimpleController', ['$scope', function ($scope) { $scope.test = 'HEY!'; }]);
You can now create the test. In the test, you must load the firstModule
module, inject $controller
, and create an instance of SimpleController
. Here is the test:
describe('SimpleController', function () { var scope = {}; var simpleCtrl; beforeEach(module('firstModule')); it("should have a scope variable of test", inject(function ($controller) { expect(scope.test).toBe(undefined); simpleCtrl = $controller('SimpleController', { $scope: scope }); expect(scope.test).toBe('HEY!'); })); });
This example will show you how to test a directive. First, create the directive:
firstModule.directive('simpleDirective', function () { return { restrict: 'E', scope:{ test: '@' }, replace: true, template: '<div>This is an example {{test}}.</div>' }; });
Next, you will need to load the module, inject $compiler
and $rootscope
, compile the directive, and finally, start the digest loop at least once to bind any values:
describe('simpleDirective', function () { var $compile, $rootScope; beforeEach(module('firstModule')); beforeEach(inject(function (_$compile_, _$rootScope_) { $compile = _$compile_; $rootScope = _$rootScope_; })); it('should have our compiled text', function () { var element = $compile('<simple-directive test="directive"></simple-directive>')($rootScope); $rootScope.$digest(); expect(element.html()).toContain('This is an example directive.'); }); });
The final testing example will test a service. First, create a service:
firstModule.factory('firstFactory', ['$http', function ($http) { return { addOne: function () { return $http.get('/test', {}) .then(function (res) { return res.data.value + 1; }); } } }]);
Next, you will have to load the module, inject $httpBackend
and the service factory, create a response, and load the response. Notice the use of $httpBackend.flush()
. This will send the response to any open requests:
describe('firstFactory', function () { var $httpBackend, testingFactory, handler; beforeEach(module('firstModule')); beforeEach(inject(function (_$httpBackend_, firstFactory) { $httpBackend = _$httpBackend_; handler = $httpBackend.expect('GET', '/test') .respond({ value: 1 }); testingFactory = firstFactory; })); it('should run the GET request and add one', function () { testingFactory.addOne() .then(function (data) { expect(data).toBe(2); }); $httpBackend.flush(); }); });
3.14.134.17