单元测试指令,测试不返回值

unit testing directive, tests not returning value

您好,我想测试一个指令,但我实在是太累了。 这是我目前的测试:

describe('pbImagePicker', function () {

    beforeEach(module('pb.campaigns.directives'));
    beforeEach(module('ui.router'));
    beforeEach(module('ui.bootstrap'));

    var $compile;
    var element;
    var $rootScope;

    beforeEach(inject(function (_$compile_, _$rootScope_, _$document_) {
        $compile = _$compile_;
        $rootScope = _$rootScope_;
        $document = _$document_;
        scope = $rootScope.$new();
    }));

    describe('template', function () {




        it('should render HTML based on scope correctly', function () {
            scope.campaign = {
                fileId: '43253',
                accountId: '3874',
                imageSource: 'http://www.gravatar.com/avatar/12345?s=40&d=identicon',
                width: '250',
                height: '250'
            };

            var element = $compile('<img data-ng-src="{{ imageSource }}" width="{{width}}" height="{{height}}" alt="Image Picker" class="img-rounded" />')(scope);
            scope.$digest();

            expect(element.html()).toEqual('<img data-ng-src="http://www.gravatar.com/avatar/12345?s=40&d=identicon" width="250" height="250" alt="Image Picker" class="img-rounded" />');
            //expect(element.attr('src')).toBe('http://www.gravatar.com/avatar/12345?s=40&d=identicon');
        });

    });


    describe('element.click()', function () {

        var campaign = {
            fileId: '43253',
            accountId: '3874',
            imageSource: 'http://www.gravatar.com/avatar/12345?s=40&d=identicon',
            width: '250',
            height: '250'
        };
        var image = {
            storageUrl: 'http://www.pressboard.com/avatar/7453',
            fileId: 6432342
        };
        var accountId = 543222;
        var pickImage = function (accountId) {
            var defer = $q.defer();
            defer.resolve(image);
            return defer.promise;
        };

        //beforeEach(function () {
        //    element = angular.element('<img data-ng-src="{{ imageSource }}" width="{{width}}" height="{{height}}" alt="Image Picker" class="img-rounded" />');
        //    compiled = $compile(element)(scope);
        //    compiled.triggerHandler('click');
        //});

        it('should resolve a promise when clicked', function () {
            scope.campaign = campaign;
            scope.accountId = accountId;
            scope.pickImage = pickImage;

            element = angular.element('<img data-ng-src="{{ imageSource }}" width="{{width}}" height="{{height}}" alt="Image Picker" class="img-rounded" />');
            compiled = $compile(element)(scope);
            compiled.triggerHandler('click');

            spyOn(scope, 'pickImage');
            scope.$digest();
            expect(scope.pickImage).toHaveBeenCalledWith(scope.accountId);
        });

        it('should assign data from resolved promise when clicked', function () {
            scope.campaign = campaign;
            scope.accountId = accountId;
            scope.pickImage = pickImage;

            element = angular.element('<img data-ng-src="{{ imageSource }}" width="{{width}}" height="{{height}}" alt="Image Picker" class="img-rounded" />');
            compiled = $compile(element)(scope);
            compiled.triggerHandler('click');

            scope.$digest();
            expect(scope.imageSource).toEqual('http://www.pressboard.com/avatar/7453');
            expect(scope.fileId).toEqual(6432342);
        });


    });

});

这是我正在测试的指令:

angular.module('pb.campaigns.directives')
    .directive('pbImagePicker', ['$window', '$document', function ($window, $document) {

        return {
            restrict: "E",
            template: '<img data-ng-src="{{ imageSource }}" width="{{width}}" height="{{height}}" alt="Image Picker" class="img-rounded" />',
            scope: {
                fileId: '=pbFileId',
                accountId: '=pbAccountId',
                defaultSrc: '@pbDefaultSrc',
                width: '@pbWidth',
                height: '@pbHeight'
            },
            controller: 'pbImagePickerController',
            link: function (scope, element, attrs) {
                scope.$watch('defaultSrc', function (value) {
                    if (value !== undefined) {
                        scope.imageSource = value;
                    }
                });

                element.click(function () {
                    scope.pickImage(scope.accountId).then(function (image) {
                        scope.imageSource = image.storageUrl;
                        scope.fileId = image.fileId;
                    }, function () {
                        console.log('Modal dismissed at: ' + new Date());
                    });
                });
            }
        };
    }]);

我得到的错误是:

Test 'pbImagePicker template:should render HTML based on scope correctly' failed
    Expected '' to equal '<img data-ng-src="http://www.gravatar.com/avatar/12345?s=40&d=identicon" width="250" height="250" alt="Image Picker" class="img-rounded" />'.

Test 'pbImagePicker element.click():should resolve a promise when clicked' failed
    Expected spy pickImage to have been called with [ 543222 ] but it was never called.

Test 'pbImagePicker element.click():should assign data from resolved promise when clicked' failed
    Expected undefined to equal 'http://www.pressboard.com/avatar/7453'.

我真的很困惑如何继续,或者我是否在正确的轨道上。 angular 用于测试指令的文档有点缺乏。如果其他人在入门时遇到问题,http://blog.ninja-squad.com/2015/01/27/5-tricks-about-directives-and-tests/ 对入门有很大帮助。

感谢任何提示!

您有几个错误...我将使用您的第一个单元测试进行解释:describe('template')

当你编译你的元素时,你将属性 "width" 设置为 {{width}} 而不是 {{campaign.width}} (基于你的范围定义)等等......

如果你想测试你的指令,你应该测试属性和 class 而不是 element.html() 因为模板是一个 <img/> 标签

更新 对指令的定义做了一些修改:

//--- CODE --------------------------
angular.module('pb.campaigns.directives', [])

.directive('pbImagePicker', ['$q', function ($q) {

  return {
    restrict: "E",
    replace : 'true',
    scope: {
        fileId: '=pbFileId',
        accountId: '=pbAccountId',
        defaultSrc: '@pbDefaultSrc',
        width: '@pbWidth',
        height: '@pbHeight'
    },
    template: '<img ' +
      'data-ng-src="{{imageSource }}" '+
      'alt="Image Picker" ' +
      'width="{{width}}" ng-click="clickFn(imageSource, fileId)" '+
      'height="{{height}}"/>',
    //controller: 'pbImagePickerController',
    link: function (scope, element, attrs) {

        scope.$watch('defaultSrc', function (value) {
            if (value !== undefined) {
                scope.imageSource = value;
            }
        });

        element.addClass('img-rounded');

        function pickImage() {
           var image = {
                storageUrl: 'http://www.pressboard.com/avatar/7453',
                fileId: 6432342
            };
            var deferred = $q.defer();
            deferred.resolve(image);
            return deferred.promise;
       }
       scope.clickFn = function () {
           pickImage()
             .then(function (image) {

                 // we encounter a non-assign error on scope.fileId
                 // due to isolated scope assignment
                 // Expression '{0}' used with directive '{1}' is non-assignable!
                 //https://docs.angularjs.org/error/$compile/nonassign
                 scope.imageSource = image.storageUrl; 
                 //scope.fileId = image.fileId;
              }, function () {
                console.log('Modal dismissed at: ' + new Date());
              });

       };
    }
  };
}]);

规范测试 模板和点击事件的完整测试

//------- SPECS - TESTING -------------
describe('pb.campaigns.directives', function () {

    var $compile,
        elem,
        element,
        $rootScope,
        $scope;

    beforeEach(function () {

        module('pb.campaigns.directives');

        inject(function ($q, _$rootScope_, _$compile_, _$controller_, _$document_) {

            scope = _$rootScope_.$new();


            scope.campaign = {
              fileId: '43253',
              accountId: '3874',
              imageSource: 'http://www.gravatar.com/avatar/12345?s=40&d=identicon',
              width: '250',
              height: '250'
            };

            element = angular.element(
                '<pb-image-picker '+
                'file-id="campaign.fileId" ' +
                'account-id="campaign.accountId" ' +
                'pb-default-src="{{campaign.imageSource}}" ' +
                'pb-width="{{campaign.width}}" ' +
                'pb-height="{{campaign.height}}">' +
                '<pb-image-picker/>'
            );
            _$compile_(element)(scope);
            scope.$digest(); 
        });
    });


    describe('template', function () {

        it('should render HTML based on scope correctly', function () {          
            scope.imageSource = scope.campaign.imageSource;            
            expect(scope.imageSource).toMatch(scope.defaultSrc);
            expect(element.attr('data-ng-src')).toEqual(scope.imageSource);
            expect(element.hasClass('img-rounded')).toBe(true);
            expect(element.attr('alt')).toMatch('Image Picker');
            expect(element.attr('width')).toEqual('250');
            expect(element.attr('height')).toEqual('250'); 
        });
    });


    describe('element click', function () {

        beforeEach (inject(function($q) {
            $scope = element.isolateScope();
            $scope.$apply();
            spyOn($scope, 'clickFn').andCallThrough();
            element.triggerHandler('click');
       }));

        afterEach(function(){
            $scope.$apply();
        });

        it('should resolve a promise when clicked', function () {
            expect($scope.clickFn).toBeDefined();
            expect($scope.clickFn).toHaveBeenCalled();
        });

        it('should assign data from resolved promise when clicked', function () {
            expect($scope.imageSource)
              .toEqual('http://www.pressboard.com/avatar/7453');
           // expect($scope.fileId).toEqual(6432342);
        }); 

    });
});

注意:正如 tasseKat 指出的那样,第一个 post 不是测试指令的正确方法(对此感到抱歉)。相反,它是试图向您展示在 it() 函数中写入的内容。希望这个版本是一个更好的例子。

我有一个 jsfiddle 来演示(供你玩)