在AngularJS中测试控制器服务调用
我对以下情况有问题: 我有一个控制器,它执行服务调用以获取不同的可用语言及其问候语。 我想测试这个控制器,我基于我在以下网站和文章中编写的测试: 当然还有AngularJs文档 但是我有一种感觉,我做了一些错误的事情,或者做了一些过度的测试 在我写的那些文章中,前3篇通过了,但第4篇(在我看来是最重要的一篇)失败了 有没有人能帮我一把或给我指出正确的方向。 似乎我读到的每一篇文章在测试内容和方法上都有所不同 控制器在AngularJS中测试控制器服务调用,angularjs,unit-testing,service,controller,karma-runner,Angularjs,Unit Testing,Service,Controller,Karma Runner,我对以下情况有问题: 我有一个控制器,它执行服务调用以获取不同的可用语言及其问候语。 我想测试这个控制器,我基于我在以下网站和文章中编写的测试: 当然还有AngularJs文档 但是我有一种感觉,我做了一些错误的事情,或者做了一些过度的测试 在我写的那些文章中,前3篇通过了,但第4篇(在我看来是最重要的一篇)失败了 有没有人能帮我一把或给我指出正确的方向。 似乎我读到的每一篇文章在测试内容和方法上都有所不同 控制器 angular.module('app') .controller('Ma
angular.module('app')
.controller('MainCtrl', function ($scope, LanguagesService) {
$scope.languages = LanguagesService.getAll();
});
describe('Controller: MainCtrl', function () {
// load the controller's module
beforeEach(module('app'));
var MainCtrl,
scope,
LanguagesService;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope, _LanguagesService_) {
scope = $rootScope.$new();
LanguagesService = _LanguagesService_;
MainCtrl = $controller('MainCtrl', {
$scope: scope,
'LanguagesService': LanguagesService
});
/*
* Spy on service
*/
spyOn(LanguagesService, 'getAll');
}));
/*
* Test 1: Is this test overkill ? As the tests wont run if the service is not injected
*/
it('should get an instance of LanguagesService', function() {
expect(LanguagesService).toBeDefined();
});
it('should attach languages to the scope', function() {
expect(scope.languages).not.toBe(null);
});
it('should have the same amount of languages as greetings', function() {
expect(scope.languages.languages.length).toBe(scope.languages.greetings.length);
});
/*
* Test 4: This test fails
*/
it('should have called LanguagesService method getAll', function() {
expect(LanguagesService.getAll).toHaveBeenCalled();
});
});
服务
angular.module('app')
.factory('LanguagesService', function () {
var lang = {};
lang.greetings = [
'Welkom bij,',
'Bienvenu chez'
];
lang.languages = [
{
name: 'Nederlands',
code: 'nl'
},
{
name: 'Français',
code: 'fr'
}
];
return {
getAll: function () {
return lang;
}
};
});
describe('Controller: MainCtrl', function () {
// load the controller's module
beforeEach(module('app'));
var MainCtrl,
scope,
LanguagesService;
var createController;
var spy;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope, _LanguagesService_) {
scope = $rootScope.$new();
LanguagesService = _LanguagesService_;
MainCtrl = $controller('MainCtrl', {
$scope: scope,
'LanguagesService': LanguagesService
});
createController = function () {
return $controller('MainCtrl', {
'$scope': scope,
'LanguagesService': LanguagesService
});
};
/*
* Spy on service
*/
spy = spyOn(LanguagesService, 'getAll');
}));
/*
* Test 1: Is this test overkill ? As the tests wont run if the service is not injected
*/
it('should get an instance of LanguagesService', function () {
expect(LanguagesService).toBeDefined();
});
it('should attach languages to the scope', function () {
expect(scope.languages).not.toBe(null);
});
it('should have the same amount of languages as greetings', function () {
expect(scope.languages.languages.length).toBe(scope.languages.greetings.length);
});
/*
* Test 4: This test fails
*/
it('should have called LanguagesService method getAll', function () {
createController();
expect(spy).toHaveBeenCalled();
});
});
控制器的我的单元测试
angular.module('app')
.controller('MainCtrl', function ($scope, LanguagesService) {
$scope.languages = LanguagesService.getAll();
});
describe('Controller: MainCtrl', function () {
// load the controller's module
beforeEach(module('app'));
var MainCtrl,
scope,
LanguagesService;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope, _LanguagesService_) {
scope = $rootScope.$new();
LanguagesService = _LanguagesService_;
MainCtrl = $controller('MainCtrl', {
$scope: scope,
'LanguagesService': LanguagesService
});
/*
* Spy on service
*/
spyOn(LanguagesService, 'getAll');
}));
/*
* Test 1: Is this test overkill ? As the tests wont run if the service is not injected
*/
it('should get an instance of LanguagesService', function() {
expect(LanguagesService).toBeDefined();
});
it('should attach languages to the scope', function() {
expect(scope.languages).not.toBe(null);
});
it('should have the same amount of languages as greetings', function() {
expect(scope.languages.languages.length).toBe(scope.languages.greetings.length);
});
/*
* Test 4: This test fails
*/
it('should have called LanguagesService method getAll', function() {
expect(LanguagesService.getAll).toHaveBeenCalled();
});
});
以下是我解决问题的方法,以供将来参考: 我放弃的第一次测试,看起来真是太过分了 我也放弃了第三个测试,因为它测试服务的输出,而不是控制器的行为,所以我将这个测试移到了我的服务单元测试中 通过在测试中模拟服务,我成功地进行了第四次测试:
'use strict';
describe('controller: MainCtrl', function() {
var ctrl, LanguagesService, $scope;
beforeEach(module('fitApp'));
beforeEach(inject(function($rootScope, $controller) {
LanguagesService = {
getAll: function() {}
};
spyOn(LanguagesService, 'getAll').and.returnValue('Foo');
$scope = $rootScope.$new();
ctrl = $controller('MainCtrl', {$scope: $scope , LanguagesService: LanguagesService });
}));
it('should call LanguagesService.getAll() once', function() {
expect(LanguagesService.getAll).toHaveBeenCalled();
expect(LanguagesService.getAll.calls.count()).toEqual(1);
});
it('should attach languages to the scope', function() {
expect($scope.languages).toEqual('Foo');
});
});
这两个测试都通过并测试控制器的行为
我希望有人能向我确认这是一个正确的选择供将来参考,以下是我解决问题的方法:
angular.module('app')
.factory('LanguagesService', function () {
var lang = {};
lang.greetings = [
'Welkom bij,',
'Bienvenu chez'
];
lang.languages = [
{
name: 'Nederlands',
code: 'nl'
},
{
name: 'Français',
code: 'fr'
}
];
return {
getAll: function () {
return lang;
}
};
});
describe('Controller: MainCtrl', function () {
// load the controller's module
beforeEach(module('app'));
var MainCtrl,
scope,
LanguagesService;
var createController;
var spy;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope, _LanguagesService_) {
scope = $rootScope.$new();
LanguagesService = _LanguagesService_;
MainCtrl = $controller('MainCtrl', {
$scope: scope,
'LanguagesService': LanguagesService
});
createController = function () {
return $controller('MainCtrl', {
'$scope': scope,
'LanguagesService': LanguagesService
});
};
/*
* Spy on service
*/
spy = spyOn(LanguagesService, 'getAll');
}));
/*
* Test 1: Is this test overkill ? As the tests wont run if the service is not injected
*/
it('should get an instance of LanguagesService', function () {
expect(LanguagesService).toBeDefined();
});
it('should attach languages to the scope', function () {
expect(scope.languages).not.toBe(null);
});
it('should have the same amount of languages as greetings', function () {
expect(scope.languages.languages.length).toBe(scope.languages.greetings.length);
});
/*
* Test 4: This test fails
*/
it('should have called LanguagesService method getAll', function () {
createController();
expect(spy).toHaveBeenCalled();
});
});
我放弃的第一次测试,看起来真是太过分了
我也放弃了第三个测试,因为它测试服务的输出,而不是控制器的行为,所以我将这个测试移到了我的服务单元测试中
通过在测试中模拟服务,我成功地进行了第四次测试:
'use strict';
describe('controller: MainCtrl', function() {
var ctrl, LanguagesService, $scope;
beforeEach(module('fitApp'));
beforeEach(inject(function($rootScope, $controller) {
LanguagesService = {
getAll: function() {}
};
spyOn(LanguagesService, 'getAll').and.returnValue('Foo');
$scope = $rootScope.$new();
ctrl = $controller('MainCtrl', {$scope: $scope , LanguagesService: LanguagesService });
}));
it('should call LanguagesService.getAll() once', function() {
expect(LanguagesService.getAll).toHaveBeenCalled();
expect(LanguagesService.getAll.calls.count()).toEqual(1);
});
it('should attach languages to the scope', function() {
expect($scope.languages).toEqual('Foo');
});
});
这两个测试都通过并测试控制器的行为
我希望有人能向我确认这是正确的选择您的测试有什么问题?你看到错误了吗?为了使用
调用
测试对象需要是间谍。我已经在beforeach(spyOn(LanguagesService,'getAll');)中放置了间谍,还是应该将其放置在另一个位置才能工作?我现在明白了。不,那应该行。jasmine给出的测试失败的原因是什么?PhantomJS 1.9.8(Linux)Controller:MainCtrl应该调用LanguageService方法getAll FAILED,而spy getAll应该被调用。at/test/spec/controllers/main.js:36您的测试有什么问题?你看到错误了吗?为了使用调用
测试对象需要是间谍。我已经在beforeach(spyOn(LanguagesService,'getAll');)中放置了间谍,还是应该将其放置在另一个位置才能工作?我现在明白了。不,那应该行。jasmine给出的测试失败的原因是什么?PhantomJS 1.9.8(Linux)Controller:MainCtrl应该调用LanguageService方法getAll FAILED,而spy getAll应该被调用。在/test/spec/controllers/main.js:36中,我在beforeEach方法中定义了一个函数createController,因此如果从测试中调用此方法,它将运行并调用getAll()函数。在beforeEach方法中,我定义了一个函数createController,因此如果从测试中调用此方法,它将运行并调用getAll()函数。
angular.module('app')
.factory('LanguagesService', function () {
var lang = {};
lang.greetings = [
'Welkom bij,',
'Bienvenu chez'
];
lang.languages = [
{
name: 'Nederlands',
code: 'nl'
},
{
name: 'Français',
code: 'fr'
}
];
return {
getAll: function () {
return lang;
}
};
});
describe('Controller: MainCtrl', function () {
// load the controller's module
beforeEach(module('app'));
var MainCtrl,
scope,
LanguagesService;
var createController;
var spy;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope, _LanguagesService_) {
scope = $rootScope.$new();
LanguagesService = _LanguagesService_;
MainCtrl = $controller('MainCtrl', {
$scope: scope,
'LanguagesService': LanguagesService
});
createController = function () {
return $controller('MainCtrl', {
'$scope': scope,
'LanguagesService': LanguagesService
});
};
/*
* Spy on service
*/
spy = spyOn(LanguagesService, 'getAll');
}));
/*
* Test 1: Is this test overkill ? As the tests wont run if the service is not injected
*/
it('should get an instance of LanguagesService', function () {
expect(LanguagesService).toBeDefined();
});
it('should attach languages to the scope', function () {
expect(scope.languages).not.toBe(null);
});
it('should have the same amount of languages as greetings', function () {
expect(scope.languages.languages.length).toBe(scope.languages.greetings.length);
});
/*
* Test 4: This test fails
*/
it('should have called LanguagesService method getAll', function () {
createController();
expect(spy).toHaveBeenCalled();
});
});