2014-02-22 81 views
8

CompanyService是:我如何模拟AngularJS单元测试中的承诺结果?

angular.module('mean').service('CompanyService', ['$http', '$rootScope', '$q', function($http, $rootScope, $q) { 
    var company = this; 
    var initializedDeferred = $q.defer(); 

    company.company_data = {} 
    company.initialized = initializedDeferred.promise; 

    company.getCompany = function() { 
    return company.company_data; 
    } 

    company.get = function (company_id) { 
    return $http({ 
     url: '/api/v1/company/' + company_id, 
     method: 'GET', 
     headers: { 
     api_key: $rootScope.api_key 
     } 
    }).success(function(response) { 
     if(response.status === 'ok') { 
     company.company_data = response.company; 
     initializedDeferred.resolve(); 
     } else { 
     alert('TBD: Need fail case'); 
     } 
    }); 
    }; 
}]); 

我的控制器:

angular.module('mean').controller('LocationController', ['$scope', '$location', '$rootScope', 'LocationService', 'UserService', 'CompanyService', '$modal', '$routeParams', function ($scope, $location, $rootScope, LocationService, UserService, CompanyService, $modal, $routeParams) { 
    $rootScope.menuItem = 'locations'; 
    $scope.contentTemplate = '/views/location/index.html'; 
    $scope.locations = []; 
    $scope.current_location = null; 
    $scope.newLocation = {}; 
    $scope.location_parent_id = $routeParams.location_parent_id; 

    $scope.index = function() { 
    CompanyService.initialized.then(function() { 
     $scope.test = 'a'; 
     var company_id = CompanyService.getCompany()._id; 
     LocationService.list(company_id, $routeParams.location_parent_id).then(function(response) { 
     if(response.data.status === 'ok') { 
      $scope.locations = response.data.locations; 
      $scope.current_location = response.data.location || null; 
     } 
     }); 
    }); 
    } 

    $scope.addLocationModal = function() { 
    $scope.location_types = ['warehouse', 'section', 'row', 'shelf', 'bin']; 
    $modal({ 
     scope: $scope, 
     template: '/views/location/addLocationModal.html', 
     show: true, 
     animation: 'am-fade-and-scale' 
    }); 
    } 

    $scope.createLocation = function() { 
    $scope.newLocation.company_id = CompanyService.getCompany()._id; 
    LocationService.create($scope.newLocation).then(function(response) { 
     if(response.data.status === 'ok') { 
     $scope.$hide(); 
     } else { 
     alert('TBD'); 
     } 
    }); 
    } 

}]); 

我的测试是:

(function() { 
    describe('LocationController', function() { 
    var $scope, $location, $rootScope, $modal, deferred, CompanyService, createController; 

    beforeEach(module('mean')); 

    beforeEach(inject(function($injector) { 
     $location = $injector.get('$location'); 
     $rootScope = $injector.get('$rootScope'); 
     $modal = $injector.get('$modal'); 
     $scope = $rootScope.$new(); 

     var $controller = $injector.get('$controller'); 

     var $q = $injector.get('$q'); 

     var params = { 
     '$scope': $scope, 
     CompanyService: jasmine.createSpyObj('CompanyService', ['initialized']) 
     } 

     params.CompanyService.initialized.andCallFake(function() { 
     deferred = $q.defer(); 
     return deferred.promise; 
     }); 


     createController = function() { 
     return $controller('LocationController', params); 
     }; 
    })); 

    it('should instantiate initial variables at the top level', function() { 
     var controller = createController(); 

     $location.path('/company/locations'); 
     expect($location.path()).toBe('/company/locations'); 
     expect($rootScope.menuItem).toBe('locations'); 
     expect($scope.contentTemplate).toBe('/views/location/index.html'); 
     expect($scope.locations.length).toEqual(0); 
     expect($scope.current_location).toBeNull(); 
     expect($scope.newLocation).toBeDefined(); 
     expect($scope.location_parent_id).not.toBeDefined(); 
    }); 

    it('should list all locations for this company', function() { 
     var controller = createController(); 
     deferred.resolve(); 
     $scope.index(); 

     expect($scope.test).toBe('a'); 
    }); 
    }); 
})(); 

但是,该resolve不工作。我得到这个错误: TypeError: 'undefined' is not an object (evaluating 'deferred.resolve')

任何帮助吗?

+0

在您最后一次测试代码的'测试'部分中,您应该列出该公司的所有位置,您有一个延迟,你从来没有创建过,并试图加解决它,它是未定义的。 (这正是错误告诉你的)。 –

+1

尝试使用'sinon.js',它的一个很棒的嘲笑框架。 – Gyandeep

+0

@BenjaminGruenbaum - 我不完全确定你的意思。是不是在整个套件范围内的'deferred',因为我在顶层范围内'var deferred'? – Shamoon

回答

6

在您的测试中,您使用伪造的CompanyService.initialized函数创建延迟对象。但是,只有当您拨打$scope.index();时才会调用此功能,该功能在deferred.resolve();行后执行。下面应该工作:

it('should list all locations for this company', function() { 
    var controller = createController(); 
    $scope.index(); // Should in turn call the fake CompanyService.initialized function that creates deferred 
    deferred.resolve(); 
    $scope.$apply(); // Fire $digest cycle to dispatch promises. 

    expect($scope.test).toBe('a'); 
}); 

更新

茉莉花不支持不属于功能性间谍。所以你的间谍设置​​无效,因为CompanyService.initialized是一个对象,而不是一个功能,所以你的andCallFake不会工作。一种解决方法是介绍你CompanyService如内吸功能:

company.isInitialized(){ return company.initialized; } 

然后你的控制器使用这里面的getter函数:

$scope.index = function() { 
    CompanyService.isInitialized().then(function() { 
    $scope.test = 'a'; 
    // Removed for brevity 
    }); 
} 

最后更新您的测试代码初始化:

var params = { 
    '$scope': $scope, 
    CompanyService: jasmine.createSpyObj('CompanyService', ['isInitialized']) 
} 
params.CompanyService.isInitialized.andCallFake(function() { 
    deferred = $q.defer(); 
    return deferred.promise; 
}); 
+1

'TypeError:'undefined'不是一个函数(评估'CompanyService.initialized.then')' – Shamoon

+0

@Shamoon Jasmine不支持监听不是函数的属性。所以你的间谍设置​​是无效的,因为'CompanyService.initialized'是一个对象而不是一个函数,所以你的'andCallFake'不会工作。使用解决方法查看更新的答案。 – Beyers

+0

现在,这是失败的:'LocationController应该实例化初始变量在顶层失败 \t预计未定义为'位置'。 \t预计未定义为'/ views/location/index.html'。' – Shamoon

相关问题