【问题标题】:Jasmine controller testing, expected spy to have been calledJasmine 控制器测试,预计 spy 已被调用
【发布时间】:2015-07-03 19:12:36
【问题描述】:

我在 AngularJS 控制器中定义了一个在初始化时调用的方法。我想使用 Jasmine ("jasmine-core": "^2.3.4", "karma": "^0.12.37") 对其进行测试。我关注互联网上的一些教程和 StackOverflow 问题,但我找不到正确的答案。请看一下这段代码:

控制器usersAddUserController

(function () {
    'use strict';

    angular.module('app.users.addUser')
        .controller('usersAddUserController', ['$scope', 'usersAddUserService', function ($scope, usersAddUserService) {

            usersAddUserService.getCountryPhoneCodes().then(function (phoneCodes) {
                $scope.phoneCodes = phoneCodes;
            });

        }]);
}());

茉莉花测试:

(function () {

    'use strict';

    describe('usersAddUserControllerUnitTest', function () {
        var scope, deferred, objectUnderTest, mockedAddUserService;

        beforeEach(module('app'));

        beforeEach(inject(function ($rootScope, $q, $controller) {
            scope = $rootScope.$new();

            function emptyPromise() {
                deferred = $q.defer();
                return deferred.promise;
            }

            mockedAddUserService = {
                getCountryPhoneCodes: emptyPromise
            };
                     
            objectUnderTest = $controller('usersAddUserController', {
                $scope: scope,
                usersAddUserService: mockedAddUserService
            });
        }));

        it('should call getCountryPhoneCodes method on init', function () {
            //when            
            spyOn(mockedAddUserService, 'getCountryPhoneCodes').and.callThrough();
            
            deferred.resolve();

            scope.$root.$digest();
            
            //then
            expect(mockedAddUserService.getCountryPhoneCodes).toHaveBeenCalled();
        });

    });
}());

运行测试后,错误信息是:

PhantomJS 1.9.8 (Windows 7 0.0.0) usersAddUserControllerUnitTest 应该在 init FAILED 上调用 getCountryPhoneCodes 方法

    Expected spy getCountryPhoneCodes to have been called.

我显然错过了一些东西,但我无法弄清楚它是什么。任何帮助将不胜感激。

【问题讨论】:

    标签: javascript angularjs karma-jasmine


    【解决方案1】:

    在将模拟传递到实例化控制器后,您正在监视模拟。

    试试这个:

    describe('usersAddUserControllerUnitTest', function () {
        var scope, deferred, objectUnderTest, mockedAddUserService, $controller;
    
        beforeEach(module('app'));
    
        beforeEach(inject(function ($rootScope, $q, _$controller_) {
            scope = $rootScope.$new();
    
            function emptyPromise() {
                deferred = $q.defer();
                return deferred.promise;
            }
    
            mockedAddUserService = {
                getCountryPhoneCodes: emptyPromise
            };
    
            $controller = _$controller_;
        }));
    
        function makeController() {    
            objectUnderTest = $controller('usersAddUserController', {
                $scope: scope,
                usersAddUserService: mockedAddUserService
            });
        }
    
        it('should call getCountryPhoneCodes method on init', function () {
            //when
    
            spyOn(mockedAddUserService, 'getCountryPhoneCodes').and.callThrough();
            makeController();
    
            deferred.resolve();
    
            scope.$root.$digest();
    
            //then
            expect(mockedAddUserService.getCountryPhoneCodes).toHaveBeenCalled();
        });
    
    });
    

    编辑感谢@juunas 注意到我的解决方案中的错误

    【讨论】:

    • 感谢您的回答,但不幸的是,您的代码仍然出现相同的错误。它对你有用吗?
    • 我实际上并没有尝试过,它只是看起来很明显的问题。你能在你的控制器构造函数上放一个断点,看看它是否真的是它正在调用的间谍?
    • 非常感谢你们!确实@juunas 是对的,我现在成功了。如果 juunas 同意,无论如何我都会接受 Martin 的回答,这对我有很大帮助:)
    • 我不太喜欢这个解决方案的两个原因: 1. 你必须调用 makeController();在每个测试中(这可以放在 beforeEach 中) 2. 代码看起来不像“提供方法”那么直接,但是好的,这只是一个品味问题。 :) 好吧,看来你必须小心事情的顺序。我不认为你有这个问题提供...
    • @timtos 您的积分有效。此解决方案是原始代码和工作测试的最短路径。
    【解决方案2】:

    您可以像这样提供模拟:

    mockedAddUserService = {
        getCountryPhoneCodes: emptyPromise
    };
    
    
    beforeEach(function () {
        module(function ($provide) {
            $provide.value('usersAddUserService', mockedAddUserService);
        });
    });
    

    编辑:

    代码应该看起来像这样(因为我无法测试它):

    (function () {
        'use strict';
    
        describe('usersAddUserControllerUnitTest', function () {        
            beforeEach(module('app'));
    
            var emptyPromise = function() {
                var deferred = $q.defer();
                return deferred.promise;
            }
    
            var mockedAddUserService = {
                getCountryPhoneCodes: emptyPromise
            };
    
            beforeEach(function () {
                module(function ($provide) {
                    $provide.value('usersAddUserService', mockedAddUserService);
                });
            });
    
            var scope;
            beforeEach(inject(function ($rootScope, $q, $controller) {
                scope = $rootScope.$new();
                $controller('usersAddUserController', {
                    $scope: scope
                });
            }));
    
            it('should call getCountryPhoneCodes method on init', function () {
                spyOn(mockedAddUserService, 'getCountryPhoneCodes').and.callThrough();
    
                scope.$root.$digest();
    
                expect(mockedAddUserService.getCountryPhoneCodes).toHaveBeenCalled();
            });
    
        });
    }());
    

    【讨论】:

    • 谢谢你的回答,但你能提供完整的代码吗?当我在我的代码中包含您的解决方案时,我得到:“TypeError: 'undefined' is not an object (evalating 'deferred.resolve')”。
    • 感谢您提供的代码,我也会尝试您的解决方案,并提供反馈意见。
    猜你喜欢
    • 2021-04-02
    • 2016-03-31
    • 2016-09-09
    • 2021-11-05
    • 1970-01-01
    • 2019-12-26
    • 1970-01-01
    • 2021-03-06
    • 2020-09-15
    相关资源
    最近更新 更多