如何测试在AngularJS单元测试中调用此嵌套函数?

时间:2014-01-31 15:19:04

标签: javascript angularjs unit-testing jasmine

我不是单元测试(C#)的新手,但我对AngularJS中的单元测试非常陌生。 我正在尝试测试我的控制器,到目前为止已经能够使几个测试正常工作,但是有一些测试证明是相当困难的。

我有$ scope方法调用我们的Authentication服务,该服务返回一个promise。在“then”函数中,我正在检查用户是否确实已经过身份验证,并且基于此我将调用一个私有函数,该函数将出去并进行其他服务调用。

目前测试失败并出现以下错误:

Expected spy getConfigurationStatuses to have been called.
Error: Expected spy getConfigurationStatuses to have been called.

如果有人能帮助我指出正确的方向,我会非常感激。我将发布以下代码 - 谢谢你的帮助!

这是我的规格(无法使用的规格是“如果用户经过身份验证,则应调用特定配置”规范:

describe('EnvironmentCtrl specs', function(){
    var $rootScope = null, $scope = null, ctrl = null;    
    var Authentication = {
        getCredentials: function(){ return true; }
    };

    var Environment = { getConfigurationStatuses: function(){ return true; } };

    beforeEach(module('ngRoute'));
    beforeEach(module('environment'));

    beforeEach(module(function($provide){
        $provide.value('SITE_ROOT', '/');
    }));

    beforeEach(inject(function(_$rootScope_, _$controller_, _$timeout_, _$location_, _$q_, _Authentication_, _Environment_){

        $rootScope = _$rootScope_;
        $controller = _$controller_;
        $timeout = _$timeout_;
        $location = _$location_;
        $q = _$q_;
        Authentication = _Authentication_;
        Environment = _Environment_;

        spyOn(Authentication, 'getCredentials').andCallThrough();
        spyOn(Environment, 'getConfigurationStatuses').andCallThrough();

        $rootScope = _$rootScope_;
        $scope = $rootScope.$new();

        ctrl = $controller('EnvironmentCtrl', {$rootScope: $rootScope,$scope: $scope, $timeout: $timeout,
            Eventor: {}, Controller: {}, Environment: Environment,Authentication: Authentication, ErrorService:{} });

    }));

    describe('When initializing the EnvironmentCtrl', function(){

        // this one works fine!
        it('should set default values on the scope object', function(){
            expect($scope.controllerName).toEqual('EnvironmentCtrl');
            expect($scope.environmentStatusType).toEqual('configurations');
            expect($scope.configurationsSelected).toBe(true);
            expect($scope.isDataLoaded).toBe(false);
        });

        // this works fine!
        it('should make a call to authenticate the user', function(){
            $scope.determineViewToDisplay();
            expect(Authentication.getCredentials).toHaveBeenCalled();
        });

        // this one doesn't work!
        it('should call specific configurations if user is authenticated', function(){
            $scope.determineViewToDisplay();
            $rootScope.isUserAuthenticated = true;
            expect(Environment.getConfigurationStatuses).toHaveBeenCalled();
        });
    });
});

以下是单元测试中涉及的三个函数:

$scope.determineViewToDisplay = function () {
    Authentication.getCredentials().then(function(){
        if ($rootScope.isUserAuthenticated === true) {
            $scope.isAnonymous = false;
            handleAuthenticatedUserView();
        } else {
            Eventor.publish('event:login', false);
            $scope.isAnonymous = true;
            handleAnonymousUserView();
        }
    }, function(err){
        ErrorService.handleError(err, null, $scope.controllerName);
    });
};

function handleAuthenticatedUserView() {
    $scope.configurationStatusTimer = $timeout(function(){
        displayConfigurationStatuses(true);
    }, 5);
}
function displayConfigurationStatuses(isAuthenticated) {
    Environment.getConfigurationStatuses(isAuthenticated).then(function(statuses){
            setConfigurationsIconStatus(statuses);
            $scope.configurationStatuses = statuses;
            $scope.isDataLoaded = true;
            amplify.store($rootScope.productCustomerName + '-configurationStatuses', statuses, {expires: 120000});            
            $rootScope.showLoadingIndicator = false;
        }, function(err){
            ErrorService.handleError(err, null, $scope.controllerName);
        });
}

2 个答案:

答案 0 :(得分:0)

如果

看起来像DetermViewToDisplay()只调用handleAuthenticatedUserView
$rootScope.isUserAuthenticated === true 

但是在你打电话之前你没有将$ rootScope.isUserAuthenticated设置为true

$scope.determineViewToDisplay()

所以handleAuthenticatedUserView()永远不会被调用,反过来displayConfigurationStatuses()永远不会被调用。

答案 1 :(得分:0)

我知道这是一个较老的问题,但是......

getCredentials方法实际上是异步操作吗?这就是为什么您会看到“应该拨打电话来验证用户身份”的原因。传递,因为对该函数的调用是同步的。 getCredentials返回一个promise并继续前进。这会导致断言在解析/拒绝处理程序运行之前运行(这是最终调用被测函数的地方)。

您可以使用'完成' (或更旧的'运行/等待')Jasmine语法中的异步操作,以确保您的断言不会运行,直到所有承诺都已解决。

另外我只是注意到在超时(更多异步)内调用displayConfigurationStatuses。您可能必须模拟$ timeout服务才能立即执行,或者使handleAnonymousUserView返回一个超时执行后解析的promise。