如何在 jasmine 中对具有外部依赖项的 ngcontroller 进行单元测试?
how to unit test a ngcontroller with external dependencies in jasmine?
我正在尝试为 angularcontroller 编写业力单元测试,这是控制器。从这里使用文件上传 (https://github.com/nervgh/angular-file-upload):
var c, di;
c = function($scope, FileUploader, UploadHelper, currentUser) {
var uploader;
$scope.uploader = uploader = new FileUploader(UploadHelper.getOptions());
uploader.onSuccessItem = function(item, response, status, headers) {
//do something
};
return this;
};
di = [ '$scope', 'FileUploader', 'UploadHelper', 'currentUser', c];
angular.module('mycontrollers').controller('DocumentsController', di);
我的茉莉花测试是这样的:
describe('Customer: Controller: DocumentsController', function() {
var $rootScope, DocumentMock, UserMock, documentsController, scope;
documentsController = $rootScope = scope = DocumentMock = UserMock = null;
beforeEach(function() {
module("mycontrollers");
return inject(function($controller, _$rootScope_, $q) {
var deferred, documents, fileUploader, uploadHelper, user;
$rootScope = _$rootScope_;
scope = $rootScope.$new();
documents = [
{
id: "doc 1",
documentType: "other"
}, {
id: "doc 2",
documentType: "pdf"
}, {
id: "doc 3",
v: "pdf"
}
];
user = {
id: 'user_id',
username: 'username'
};
UserMock = {
currentUser: function() {
return user;
}
};
fileUploader = {};
uploadHelper = {};
return documentsController = $controller('DocumentsController', {
documents: documents,
$scope: scope,
FileUploader: fileUploader,
UploadHelper: uploadHelper,
currentUser: UserMock
});
});
});
return describe("On creation", function() {
return it("should assign documents to scope", function() {
return expect(1).toEqual(1);
});
});
});
我有一个名为 QuestionsAttachments 的服务,它有一些依赖项:
s = function(API, $http, $q, $timeout) {
var QuestionsAttachments;
return QuestionsAttachments = (function() {
function QuestionsAttachments(data) {
angular.extend(this, data);
}
QuestionsAttachments["new"] = function(data) {
return new QuestionsAttachments(data);
};
return QuestionsAttachments;
})();
};
di = ['API', '$http', '$q', '$timeout', s];
这个问题是:如何模拟 API ,$http, $q, $timeout ?
这是一个 link 的 plunkr:
http://plnkr.co/edit/BT8SJgW6ejcPw6xo1cpI?p=preview
Basically what you do is you set up a spy on the method of that
service.
All you have to do is get the injetable service (the real implementation)
and the just mock/stub the function(s) you need. No need to create your own
mock objects like so: uploadHelper = {}
spyOn(uploadHelper, 'getOptions').andCallFake(function(){
// return data here
});
// or you could use .Return({ /* some object */})
You can read more about jasmine and spies in particular here
话虽如此,我必须说提供的代码看起来确实有点不完整。
例如,根据示例,如果您没有创建任何模块并且您直接使用 getter 语法而不是 angular.module('mycontrollers', []);
.
其次,在您的测试中,您试图在控制器实例化期间注入 documents
,但这将永远行不通,因为您没有可注入的参数 'documents'在您的控制器定义中。
所以您遇到的问题很可能是由于上面的概述。
无论如何,一个完整的例子,重点是如何 mock/stub/spy on UploadHelper.getOptions() 可能看起来像这样。这是一个可用的 Plunkr http://plnkr.co/edit/RfQFsaLq8XV2FvMT3xr0?p=preview
实施
angular
.module('mycontrollers',[])
.controller('DocumentsController', documentsController);
documentsController.$inject = ['$scope', 'FileUploader', 'UploadHelper', 'currentUser'];
function documentsController($scope, FileUploader, UploadHelper, currentUser){
var uploader;
$scope.uploader = uploader = new FileUploader(UploadHelper.getOptions());
uploader.onSuccessItem = function(item, response, status, headers){
// do something
};
}
测试
describe('Customer: Controller: DocumentsController', function(){
beforeEach(function(){
module('mycontrollers');
inject(function($injector){
// fetch our dependencies
this.$controller = $injector.get('$controller');
this.FileUploader = $injector.get('FileUploader');
this.UploadHelper = $injector.get('UploadHelper');
this.$scope = $injector.get('$rootScope').$new();
this.user = {
id: 'user_id',
username: 'username'
};
this.UserMock = {
currentUser: function(){
return user;
}
}
});
});
function initController(context){
return context.$controller('DocumentsController', {
$scope: context.$scope,
FileUploader: context.FileUploader,
UploadHelper: context.UploadHelper,
currentUser: context.UserMock
});
}
describe('On creation', function(){
it('should call UploadHelper.getOptions()', function(){
// spy on getOptions and when it is getting called, return whatever we've specified
spyOn(this.UploadHelper, 'getOptions').andReturn({ /* some options */});
// instantiate the controller
initController(this);
expect(this.UploadHelper.getOptions).toHaveBeenCalled();
expect(this.$scope.uploader.onSuccessItem).toBeDefined();
});
});
});
这是一个为注入服务创建完整模拟的简短示例。
var mockInjectedProvider;
beforeEach(function() {
module('myModule');
});
beforeEach(inject(function(_injected_) {
mockInjectedProvider = mock(_injected_);
});
beforeEach(inject(function(_base_) {
baseProvider = _base_;
}));
it("injectedProvider should be mocked", function() {
mockInjectedProvider.myFunc.andReturn('testvalue');
var resultFromMockedProvider = baseProvider.executeMyFuncFromInjected();
expect(resultFromMockedProvider).toEqual('testvalue');
});
//mock all service methods
function mock(angularServiceToMock) {
for (var i = 0; i < Object.getOwnPropertyNames(angularServiceToMock).length; i++) {
spyOn(angularServiceToMock, Object.getOwnPropertyNames(angularServiceToMock)[i]);
}
return angularServiceToMock;
}
我正在尝试为 angularcontroller 编写业力单元测试,这是控制器。从这里使用文件上传 (https://github.com/nervgh/angular-file-upload):
var c, di;
c = function($scope, FileUploader, UploadHelper, currentUser) {
var uploader;
$scope.uploader = uploader = new FileUploader(UploadHelper.getOptions());
uploader.onSuccessItem = function(item, response, status, headers) {
//do something
};
return this;
};
di = [ '$scope', 'FileUploader', 'UploadHelper', 'currentUser', c];
angular.module('mycontrollers').controller('DocumentsController', di);
我的茉莉花测试是这样的:
describe('Customer: Controller: DocumentsController', function() {
var $rootScope, DocumentMock, UserMock, documentsController, scope;
documentsController = $rootScope = scope = DocumentMock = UserMock = null;
beforeEach(function() {
module("mycontrollers");
return inject(function($controller, _$rootScope_, $q) {
var deferred, documents, fileUploader, uploadHelper, user;
$rootScope = _$rootScope_;
scope = $rootScope.$new();
documents = [
{
id: "doc 1",
documentType: "other"
}, {
id: "doc 2",
documentType: "pdf"
}, {
id: "doc 3",
v: "pdf"
}
];
user = {
id: 'user_id',
username: 'username'
};
UserMock = {
currentUser: function() {
return user;
}
};
fileUploader = {};
uploadHelper = {};
return documentsController = $controller('DocumentsController', {
documents: documents,
$scope: scope,
FileUploader: fileUploader,
UploadHelper: uploadHelper,
currentUser: UserMock
});
});
});
return describe("On creation", function() {
return it("should assign documents to scope", function() {
return expect(1).toEqual(1);
});
});
});
我有一个名为 QuestionsAttachments 的服务,它有一些依赖项:
s = function(API, $http, $q, $timeout) {
var QuestionsAttachments;
return QuestionsAttachments = (function() {
function QuestionsAttachments(data) {
angular.extend(this, data);
}
QuestionsAttachments["new"] = function(data) {
return new QuestionsAttachments(data);
};
return QuestionsAttachments;
})();
};
di = ['API', '$http', '$q', '$timeout', s];
这个问题是:如何模拟 API ,$http, $q, $timeout ?
这是一个 link 的 plunkr: http://plnkr.co/edit/BT8SJgW6ejcPw6xo1cpI?p=preview
Basically what you do is you set up a spy on the method of that service. All you have to do is get the injetable service (the real implementation) and the just mock/stub the function(s) you need. No need to create your own mock objects like so:
uploadHelper = {}
spyOn(uploadHelper, 'getOptions').andCallFake(function(){ // return data here }); // or you could use .Return({ /* some object */})
You can read more about jasmine and spies in particular here
话虽如此,我必须说提供的代码看起来确实有点不完整。
例如,根据示例,如果您没有创建任何模块并且您直接使用 getter 语法而不是 angular.module('mycontrollers', []);
.
其次,在您的测试中,您试图在控制器实例化期间注入 documents
,但这将永远行不通,因为您没有可注入的参数 'documents'在您的控制器定义中。
所以您遇到的问题很可能是由于上面的概述。
无论如何,一个完整的例子,重点是如何 mock/stub/spy on UploadHelper.getOptions() 可能看起来像这样。这是一个可用的 Plunkr http://plnkr.co/edit/RfQFsaLq8XV2FvMT3xr0?p=preview
实施
angular
.module('mycontrollers',[])
.controller('DocumentsController', documentsController);
documentsController.$inject = ['$scope', 'FileUploader', 'UploadHelper', 'currentUser'];
function documentsController($scope, FileUploader, UploadHelper, currentUser){
var uploader;
$scope.uploader = uploader = new FileUploader(UploadHelper.getOptions());
uploader.onSuccessItem = function(item, response, status, headers){
// do something
};
}
测试
describe('Customer: Controller: DocumentsController', function(){
beforeEach(function(){
module('mycontrollers');
inject(function($injector){
// fetch our dependencies
this.$controller = $injector.get('$controller');
this.FileUploader = $injector.get('FileUploader');
this.UploadHelper = $injector.get('UploadHelper');
this.$scope = $injector.get('$rootScope').$new();
this.user = {
id: 'user_id',
username: 'username'
};
this.UserMock = {
currentUser: function(){
return user;
}
}
});
});
function initController(context){
return context.$controller('DocumentsController', {
$scope: context.$scope,
FileUploader: context.FileUploader,
UploadHelper: context.UploadHelper,
currentUser: context.UserMock
});
}
describe('On creation', function(){
it('should call UploadHelper.getOptions()', function(){
// spy on getOptions and when it is getting called, return whatever we've specified
spyOn(this.UploadHelper, 'getOptions').andReturn({ /* some options */});
// instantiate the controller
initController(this);
expect(this.UploadHelper.getOptions).toHaveBeenCalled();
expect(this.$scope.uploader.onSuccessItem).toBeDefined();
});
});
});
这是一个为注入服务创建完整模拟的简短示例。
var mockInjectedProvider;
beforeEach(function() {
module('myModule');
});
beforeEach(inject(function(_injected_) {
mockInjectedProvider = mock(_injected_);
});
beforeEach(inject(function(_base_) {
baseProvider = _base_;
}));
it("injectedProvider should be mocked", function() {
mockInjectedProvider.myFunc.andReturn('testvalue');
var resultFromMockedProvider = baseProvider.executeMyFuncFromInjected();
expect(resultFromMockedProvider).toEqual('testvalue');
});
//mock all service methods
function mock(angularServiceToMock) {
for (var i = 0; i < Object.getOwnPropertyNames(angularServiceToMock).length; i++) {
spyOn(angularServiceToMock, Object.getOwnPropertyNames(angularServiceToMock)[i]);
}
return angularServiceToMock;
}