【问题标题】:How do I test that this nested function was called in an AngularJS unit test?如何测试在 AngularJS 单元测试中调用了这个嵌套函数?
【发布时间】:2014-01-31 15:19:04
【问题描述】:

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

我有 $scope 方法调用我们的身份验证服务,该服务返回一个承诺。在“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);
        });
}

【问题讨论】:

    标签: javascript angularjs unit-testing jasmine


    【解决方案1】:

    看起来确定ViewToDisplay() 只调用handleAuthenticatedUserView if

    $rootScope.isUserAuthenticated === true 
    

    但您直到调用后才将 $rootScope.isUserAuthenticated 设置为 true

    $scope.determineViewToDisplay()
    

    所以 handleAuthenticatedUserView() 永远不会被调用,而 displayConfigurationStatuses() 永远不会被调用。

    【讨论】:

    • 感谢您的回复!星期一早上我会先试一试:-)
    • 早上好,我按照您的建议进行了尝试,将 $rootScope.isUserAuthenticated = true 移动到在我的测试中调用 $scope.determineViewToDisplay() 之前,我仍然收到“预期的间谍 getConfigurationsStatuses叫了。” :-(
    【解决方案2】:

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

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

    您可以在 Jasmine 中将“done”(或更旧的“runs/waitsFor”)语法与异步操作一起使用,以确保在所有承诺都得到解决之前不会运行您的断言。

    我还注意到 displayConfigurationStatuses 在超时内被调用(更异步)。您可能必须模拟 $timeout 服务才能立即执行,或者让 handleAnonymousUserView 返回一个承诺,一旦超时执行,该承诺就会解决。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-10
      • 2010-09-24
      • 1970-01-01
      • 2021-10-08
      相关资源
      最近更新 更多