[Unit Testing] AngularJS Unit Testing - Karma

Install Karam:

npm install -g karma
npm install -g karma-cli

 

Init Karam:

karma init

 

First test:

1. Add test file to the karma.conf.js:

    // list of files / patterns to load in the browser
    files: [
        'test/hello.js'
    ],

2. Add test file:

describe('First Unit testing with Karma', function() {
    it('sholud work', function() {
        expect(true).toBe(false);
    })
})

Of course, it will failed. 

Then we make it pass:

describe('First Unit testing with Karma', function() {
    it('sholud work', function() {
        expect(true).toBe(true);
    })
})

 

Testing with AngularJS


 

Install angular-mocks:

npm install angular-mocks

 

Include the angular.js and angular-mocks.js file before the test file:

    // list of files / patterns to load in the browser
    files: [
        'test/hello.js',
        'bower_components/angular/angular.js',
        'node_modules/angular-mocks/angular-mocks.js',
        'test/anuglarjs.js'
    ],

 

Write the test file:

复制代码
describe('Testing with AngularJS', function() {
    //Want $scope and element can be used globally
    var $scope,
        element;

    //We need to inject $compile, $rootScope
    beforeEach(inject(function($compile, $rootScope) {
        $scope = $rootScope;
        //create an 'angular element'
        element = angular.element("<div>{{2+2}}</div>");
        //then compile it to real angular element
        //also it requires link function and scope
        element = $compile(element)($rootScope);
    }));

    it('should equals to 4', function() {
        //make sure to $digest() it to get the change
        $scope.$digest();
        expect(element.html()).toBe("5");  // change to "4" to make it pass
    })
})
复制代码

Now, "4" is not "5", then it will failed, switch to "4" to make it pass.

 

Testing Directive:


 

    .directive('aGreatEye', function () {
        return {
            restrict: 'E',
            replace: true,   //Important to add replace: true, otherwise, it would be <h1 class="ng-binding">...</h1>"
            template: '<h1>lidless, wreathed in flame, {{1 + 1}} times</h1>'
        };
    });

 

复制代码
describe('Testing directive', function() {
    var $scope,
        $compile,
        element;

    beforeEach(module('app'));

    beforeEach(inject(function(_$compile_, _$rootScope_) {
        $compile = _$compile_;
        $scope = _$rootScope_;
    }));

    it("Replaces the element with the appropriate content", function() {
        element = $compile('<a-great-eye></a-great-eye>')($scope);
        $scope.$digest();
        expect(element.html()).toBe('lidless, wreathed in flame, 2 times');
    })
});
复制代码

Underscore notation: The use of the underscore notation (e.g.: _$rootScope_) is a convention wide spread in AngularJS community to keep the variable names clean in your tests. That's why the $injector strips out the leading and the trailing underscores when matching the parameters. The underscore rule applies only if the name starts and ends with exactly one underscore, otherwise no replacing happens.

 

Testing Directives With External Templates:


 

1. Install 'karma-ng-html2js-preprocessor': https://github.com/karma-runner/karma-ng-html2js-preprocessor

npm install karma-ng-html2js-preprocessor --save-dev

 

2. Add config to karma.config.js:

复制代码
    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
        '*.html': ['ng-html2js']
    },

      ngHtml2JsPreprocessor: {
          // strip this from the file path
          stripPrefix: '/'  //because currently all the html files are located in / root
      },
复制代码

 

3. Add html into beforeEach:

复制代码
describe('Testing directive', function() {
    var $scope,
        $compile,
        element;

    beforeEach(module('app'));

    // Add html file here
    beforeEach(module('directivetest.html'));

    beforeEach(inject(function(_$compile_, _$rootScope_) {
        $compile = _$compile_;
        $scope = _$rootScope_;
    }));

    it("Replaces the element with the appropriate content", function() {
        element = $compile('<a-great-eye></a-great-eye>')($scope);
        $scope.$digest();
        expect(element.html()).toBe('lidless, wreathed in flame, 2 times');
    })
});
复制代码

 

复制代码
    .directive('aGreatEye', function () {
        return {
            restrict: 'E',
            replace: true,
            templateUrl: 'directivetest.html'
        };
    });

   /*directivetest.html*/
   <h1>lidless, wreathed in flame, {{1 + 1}} times</h1>
复制代码

 

Testing Directive Scope:


 

1. Check after a click event, scope value changed:

复制代码
    .directive('aGreatEye', function () {

        return {
            restrict: 'EA',
            replace: true,
            templateUrl: 'directivetest.html',
            link: function(scope, element) {
                scope.isClicked = false;
                element.bind('click', function() {
                    scope.isClicked = true;
                })
            }
        };
    });
复制代码

 

    it("isClicked is true after click", function() {
        element = $compile('<a-great-eye></a-great-eye>')($scope);
        $scope.$digest();
        element.triggerHandler('click');
        expect($scope.isClicked).toBe(true);
    });

 

2. Isolated scope testing:

复制代码
    .directive('aGreatEye', function () {

        return {
            restrict: 'EA',
            replace: true,
            scope: {},
            templateUrl: 'directivetest.html',
            link: function(scope, element) {
                scope.isClicked = false;
                element.bind('click', function() {
                    scope.isClicked = true;
                })
            }
        };
    });
复制代码

 

    it("isolated scope testing", function() {
        element.triggerHandler('click');
        expect(element.isolateScope().isClicked).toBe(true);
    });

 

You can no long use "element.scope()" or "$scope", you should use "element.isolateScope()".

 

Testing Directive Scope Binding:


 

Tow ways binding:

复制代码
    .directive('aGreatEye', function () {

        return {
            restrict: 'EA',
            replace: true,
            scope: {
                flavor: "=" //Accpet an object
            },
            templateUrl: 'directivetest.html',
            link: function(scope, element) {

                element.bind('click', function() {
                    scope.isClicked = true;

                    scope.flavor.message += " World!";
                })
            }
        };
    });
复制代码

 

复制代码
    beforeEach(inject(function(_$compile_, _$rootScope_) {
        $compile = _$compile_;
        $scope = _$rootScope_;
        $scope.goPro = {message: "Hello"};
        element = $compile('<div a-great-eye flavor="goPro"></div>')($scope);
        $scope.$digest();
    }));


    it("after click the scope.data should be Hello World!", function() {
        element.triggerHandler('click');
        expect($scope.goPro.message).toBe('Hello World!');
        expect(element.isolateScope().flavor.message).toBe("Hello World!");
    });
复制代码

 

Testing Speed:


 Using ddescriber plugin to speed up unit testing.

Install the plugin "ddscriber for jasmine". Ctrl+Shift+D, to open the dailog. In the dialog you can choose which test to be included or not.

The code is modfied as: 

复制代码
/**
 * Created by Answer1215 on 1/16/2015.
 */
ddescribe('Testing directive', function() {
    var $scope,
        $compile,
        element;

    beforeEach(module('app'));

    beforeEach(module('directivetest.html'));

    beforeEach(inject(function(_$compile_, _$rootScope_) {
        $compile = _$compile_;
        $scope = _$rootScope_;
        $scope.goPro = {message: "Hello"};
        element = $compile('<div a-great-eye flavor="goPro"></div>')($scope);
        $scope.$digest();
    }));

    iit("Replaces the element with the appropriate content", function() {
        expect(element.html()).toContain("lidless, wreathed in flame, 2 times");
    });

    //it("isClicked is true after click", function() {
    //    element.triggerHandler('click');
    //    expect($scope.isClicked).toBe(true);
    //});
    
    iit("isolated scope testing", function() {
        element.triggerHandler('click');
        //expect(element.isolateScope().isClicked).toBe(true);
    });


    xit("after click the scope.data should be Hello World!", function() {
        element.triggerHandler('click');
        expect($scope.goPro.message).toBe('Hello World!');
        //expect(element.isolateScope().flavor.message).toBe("Hello World!");
    });
});



xdescribe("error driective testing", function() {

    var $scope, $compile, element;

    beforeEach(module('app'));

    beforeEach(module('error.html'));

    beforeEach(inject(function(_$compile_, _$rootScope_) {
        $scope = _$rootScope_;
        $compile = _$compile_;

        element = $compile('<error></error>')($scope);
        $scope.$digest();
    }));

    iit("Should contains alert-danger class", function() {
        expect(element.find('div').hasClass('alert-danger')).toBe(true);
    });
});
复制代码

 

ddscribe / iit  -- include

xdscribe / xit -- exclude

 

Testing Service:


 Testing service, you need to inject the service into beforeEach.

复制代码
ddescribe('testing service', function() {
    var SmithService;

    beforeEach(module('app'));
    beforeEach(inject(function(_SmithService_) {
        SmithService = _SmithService_;
    }));

    iit('should append Smith after each name', function() {
        expect(SmithService.getName("John")).toBe("John Smith");
    })
});
复制代码

This testing case is test whenever use a service to get a name we will append " Smith" to the end.

 

  .service('SmithService', function() {
        var SmithService = {};
        SmithService.getName = function(name) {
            return name + " Smith";
        }

        return SmithService;
    });

 

Testing Controller:


 Testing controller, you need to inject the $controller into beforeEach. And get ctrl instence by using:

appCtrl = $controller("AppCtrl")

 

复制代码
ddescribe('Test Controller', function() {
    var ctrl;
    beforeEach(module('app'));
    beforeEach(inject(function(_$controller_) {
        ctrl = _$controller_("TestCtrl");
    }));
    iit('Test Controller', function() {
        expect(ctrl.message).toBe("Hello");
    });
});
复制代码

 

    .controller('TestCtrl', function() {
        var vm = this;

        vm.message = "Hello";
    });

 

posted @   Zhentiw  阅读(754)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示