【问题标题】:Jasmine spyOn not working properly on AngularJS directiveJasmine spyOn 在 AngularJS 指令上无法正常工作
【发布时间】:2018-09-13 17:44:51
【问题描述】:

我正在开发一个 AngularJS 应用程序,我在具体指令中遇到了 Jasmine 的 SpyOn 的一些问题。

该指令非常简单,只需调用服务的方法,当它解析/拒绝承诺时,就会采取相应的行动,设置一些值或另一个值。

问题:当我尝试模拟 SignatureService.getSignatureData 时,SpyOn 无法按预期工作,并且就像我在 getSignatureData 上调用 jasmine 的 callThrough 方法一样。

我一直在其他指令和服务中使用 spyOn 和 mocks,这些都没有问题。

这两天我一直在尝试解决这个问题,与其他解决方案和用户的答案进行比较,但我找不到有效的解决方案。

这是我的代码:

AngularJS 指令代码

angular
    .module('module_name')
    .directive('signatureDirective', signatureDirective);

angular
    .module('GenomcareApp_signature')
    .controller('signatureDController', signatureDController);

function signatureDirective() {
    return {
        restrict: 'E',
        templateUrl: 'components/signature/signature.directive.html',
        controller: signatureDController,
        controllerAs: 'ctrl',
        bindToController: true
    };
}

signatureDController.$inject = [
    '$scope',
    '$rootScope',
    '$location',
    'SignatureService'
];

function signatureDController($scope, $rootScope, $location, SignatureService) {
    var controller = this;

    $scope.$on('pdfFileLoadSuccessfully', function (data) {
        console.log(data);
        controller.loadPdfSucceed = true;
    });

    $scope.$on('pdfFileLoadFails', function (data) {
        console.error(data);
        controller.loadPdfError = true;
    });

    function loadDirectiveInitData() {
        var  queryParameters = atob($location.search().data);
        controller.email = queryParameters.split(';')[0];
        controller.phone = queryParameters.split(';')[1];
        controller.docid = queryParameters.split(';')[2];

        SignatureService.getSignatureData(controller.email, controller.phone, controller.docid)
            .then(
                function (data) {
                    console.log(data);
                    controller.stampTime = data.stamp_time;
                    controller.fileUrl = data.original_file.url;
                },
                function (error) {
                    console.error(error);
                    controller.error = true
                })
            .finally(
                function () {
                    controller.endLoad = true;
                })
    }

    loadDirectiveInitData();
}

Jasmine 测试代码

'use strict';
/* global loadJSONFixtures */


describe('Test :: Signature directive', function () {
    beforeEach(angular.mock.module('app'));
    beforeEach(module('translateNoop'));

    var $q, $compile, $rootScope, controller, $scope, $httpBackend, $location, SignatureService;

    beforeEach(angular.mock.inject(function (_$controller_, _$q_, _$rootScope_, _$location_, _$compile_, _$httpBackend_, _SignatureService_) {
        $q = _$q_;
        $compile = _$compile_;
        $location = _$location_;
        $scope = _$rootScope_.$new();
        $httpBackend = _$httpBackend_;
        SignatureService = _SignatureService_;

        spyOn($location, 'search').and.returnValue({data: 'dGVzdEB0ZXN0LmNvbTsrMzQ2NjY2NjY2NjY7WG9TUFFnSkltTWF2'});
        $httpBackend.whenGET('components/signature/signature.directive.html').respond(200, '');

        controller = _$controller_('signatureDController', {$scope: $scope});
    }));

    describe('Testing directive', function () {
        it('Init data should be set when promise resolves/rejects', function (done) {
            // SpyOn DOES NOT MOCK THE SERVICE METHOD
            spyOn(SignatureService, 'getSignatureData').and.callFake(function () {
                return $q.resolve({...})
            });

            var element = angular.element('<signature-directive></signature-directive>');
            element = $compile(element)($scope);
            $scope.$digest();
            done();

            // ... some expect stuff
        });
    });
});

如果有人能给我一些建议或解决方案,我将非常感激。

非常感谢。

UPDATE1:我不知道为什么,但是如果我没有在全局beforeEach 中声明controller 变量,Jasmine 的 spyOn 会按我的预期模拟该方法。

现在的问题是如何让控制器测试控制器值是否按预期设置。

【问题讨论】:

    标签: javascript angularjs unit-testing angularjs-directive jasmine


    【解决方案1】:

    尝试使用$q.defer(),这是一个例子:

    it('Init data should be set when promise resolves/rejects', function (done) {
            // SpyOn DOES NOT MOCK THE SERVICE METHOD
            spyOn(SignatureService, 'getSignatureData').and.callFake(function () {
                let deferred = $q.defer();
                deferred.resolve({...});
                return deferred.promise;
            });
    
            var element = angular.element('<signature-directive></signature-directive>');
            element = $compile(element)($scope);
            $scope.$digest();
            done();
    
            // ... some expect stuff
        });
    

    【讨论】:

    • 非常感谢您的回答。实际上,$q.resolve({...}) 也返回了一个已解决的承诺,所以......它实际上是一样的。
    【解决方案2】:

    嗯...我意识到问题在于控制器是首先被创建的,并且当服务被模拟时,控制器会忽略它。 当我将服务的spyOn 粘贴到全局beforeEach 时,这个想法是偶然产生的。

    所以我决定在每个 describebeforeEach 中创建一个新的控制器实例和对应的 spyOn 以及所需的结果。

    它有效。也许这不是最好的方法,我鼓励任何有答案的人发布它。我将永远伟大。

    这是我的最终测试代码:

    describe('Test :: Signature directive', function () {
        beforeEach(angular.mock.module('app'));
        beforeEach(module('translateNoop'));
    
        var $q, $compile, $rootScope, $scope, $httpBackend, $location, SignatureService, test_fixture;
    
        beforeEach(angular.mock.inject(function (_$q_, _$rootScope_, _$location_, _$compile_, _$httpBackend_, _SignatureService_) {
            $q = _$q_;
            $compile = _$compile_;
            $location = _$location_;
            $scope = _$rootScope_.$new();
            $httpBackend = _$httpBackend_;
            SignatureService = _SignatureService_;
            // controller = _$controller_;
    
            spyOn($location, 'search').and.returnValue({data: 'dGVzdEB0ZXN0LmNvbTsrMzQ2NjY2NjY2NjY7WG9TUFFnSkltTWF2'});
            $httpBackend.whenGET('components/signature/signature.directive.html').respond(200, '');
        }));
    
        describe('Testing directive when service resolve promise', function () {
            var controller;
            beforeEach(inject(function(_$controller_) {
                spyOn(SignatureService, 'getSignatureData').and.callFake(function () {
                    return $q.resolve({...})
                });
    
                controller = _$controller_('signatureDController', {$scope: $scope})
            }));
    
            it('Init data should be set', function () {
                // spyOn($location, 'search').and.callThrough();
                var element = angular.element('<signature-directive></signature-directive>');
                element = $compile(element)($scope);
                $scope.$digest();
    
                // ... some expect(...).toEqual(...) stuff and more
    
            });
        });
    });
    

    感谢您的宝贵时间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-10-12
      • 2015-10-29
      • 1970-01-01
      • 1970-01-01
      • 2015-10-27
      • 1970-01-01
      • 2019-07-11
      相关资源
      最近更新 更多