使用 jasmine 在 Angular 控制器中模拟服务依赖

Mocking a service dependency in an Angular controller with jasmine

我一直在尝试使用 karma 和 jasmine 在 angular 中开始单元测试,并且我一直在绞尽脑汁试图解决如何测试具有依赖关系的控制器的问题。我尝试用 jasmine spyObj 模拟间谍并将其注册到 beforeEach 挂钩中,但由于某种原因,间谍未被识别。

代码如下:

angular.module('testModule', [])
.controller('TestController', [
    '$scope',
    'TestService',
    function ($scope, TestService) {
        $scope.data = TestService.load();
    }])

.factory('TestService', function () {
    return {
        load: function(){
            return "foo";
        }
    }
});

这是测试

describe('TestController', function() {

var $controller, $scope, TestService;

beforeEach(module('testModule'), function($provide){
    TestService = jasmine.createSpyObj("TestService", ["load"]);
    TestService.load.andReturn("bar");
    $provide.value("TestService", TestService)
});

beforeEach(inject(function(_$controller_, $rootScope, _TestService_) {
    $scope = $rootScope.$new();
    TestService = _TestService_;
    $controller = _$controller_('TestController', {
        $scope: $scope,
        TestService: TestService
    });
}));

it('should set $scope.data to bar when TestService.load is called', function() {
    expect(TestService.load).toHaveBeenCalled();
    expect($scope.data).toEqual("bar");
}); });

测试中的两个断言均失败。

当我调用 expect(TestService.load).toHaveBeenCalled();

时得到 'Error: Expected a spy, but got Function'

如果我调用 expect($scope.data).toEqual("bar"),我会得到 Expected 'foo' to equal 'bar'。 "Foo" 来自实际服务,而不是间谍对象。

感谢您的帮助。

在您的 beforeEach 中,您在 _TestService_ 中注入,然后通过以下方式覆盖您在之前 beforeEach 中声明的那个:

TestService = _TestService_;

删除该代码,您的测试应该会成功。

也没有必要这样做:

$provide.value("TestService", TestService)

基本上,当您手动注入不必要的东西时,您正在尝试使用 Angular 的依赖注入。

而不是 jasmine.createSpyObj,使用 $injector 提供的现有服务然后只模拟单个方法会更容易。您可以使用 spyOn 代替:

describe('TestController', function() {

var $controller, $scope, TestService;

beforeEach(module('testModule'));

beforeEach(inject(function(_$controller_, $rootScope, _TestService_) {
    $scope = $rootScope.$new();
    TestService = _TestService_;
    spyOn(TestService, 'load').and.returnValue('bar');
    $controller = _$controller_('TestController', {
        $scope: $scope,
        TestService: TestService
    });
}));

it('should set $scope.data to bar when TestService.load is called',       function() {
        expect(TestService.load).toHaveBeenCalled();
        expect($scope.data).toEqual("bar");
    }); 
});