describe('ServiceBeingTested Name', (): void => {
var mockFirstDependency;
var mockSecondDependency;
var TestedService;
//Mock all dependencies
beforeEach((): void => {
angular.mock.module('moduleServiceIsIn'); //Register the module which the service is in
mockFirstDependency = sinon.stub(new MockFirstDependency());//Sinon if useful for mocking
mockSecondDependency = sinon.stub(new MockSecondDependency());
angular.mock.module(($provide): void => {
$provide.value('FirstDependency', mockFirstDependency);
$provide.value('SecondDependency', mockSecondDependency);
});
});
beforeEach(inject(
['TestedService', (_TestedService_: TestedService): void => {
TestedService = _TestedService_;
}]));
//Describe each method in the service
describe('method to test', (): void => {
it("should...", () => {
//testing goes in here
expect(TestedService.someMethod()).toBe("some value");
});
});
这是一个如何测试 Angular 服务的简单示例。在这种情况下,该服务称为 TestedService。
首先你会看到三个变量声明。声明前两个以模拟此服务的两个依赖项。(假设此服务有两个依赖项)。最后一个变量声明将分配给正在测试的实际服务。
现在在 beforeEach 中:
angular.mock.module
这一行注册了你正在测试的服务所在的模块。这一行非常重要。
接下来的两行使用 Sinon.js 模拟正在测试的服务的依赖关系。我建议查看 Sinon.js
它的工作方式是我们有一个名为“FirstDependency”的依赖项,我创建了一个存根并称为“MockedFirstDependency”,在这里我创建了它的一个实例。
现在是下一部分(包含 $provide 的部分)
$provide.value('FirstDependency', mockFirstDependency);
上面这行代码的作用是告诉 Angular 每次使用 FirstDependency 服务时,都应该使用 mockFirstDependency。
现在在下一个之前,我所做的就是注入我正在测试的实际服务并将其分配给我的全局变量。
然后开始测试
编辑:测试控制器
describe('mainCtrl', (): void => {
var $controllerConstructor;
var MainCtrlInstance;
var mockScope;
var mockState;
var mockStates;
var mockGlobalData;
beforeEach(() => {
angular.mock.module('mainCtrlModule');
mockScope = sinon.stub(new MockScope());
mockState = sinon.stub(new MockState());
mockStates = sinon.stub(new MockState());
mockGlobalData = sinon.stub(new MockGlobalData());
inject(($controller: ng.IControllerService): void => {
$controllerConstructor = $controller;
});
//Constructs the controller, all dependencies must be injected here
MainCtrlInstance = $controllerConstructor('mainCtrl',
{
'$Scope': mockScope,
'$State': mockState,
'States': mockStates,
'srvGlobalData': mockGlobalData
}
);
});
describe('Method to Tests', (): void => {
it("should...", (): void => {
//Testing Begins
expect(MainCtrlInstance.method()).toBe("some value");
});
});
});
编辑:测试指令
首先,您需要使用以下命令安装 Html2JsPreprocessor:npm install karma-ng-html2js-preprocessor --save-dev,如 here 所述。
karma.conf.js
files: [
//Obviously include all of your Angular files
//but make sure to include your jQuery before angular.js
"directory/to/html/directive.html", // include html for directive
"directive.js" // file directive is contained in
"directive.spec.js"" // spec file
]
// include the directive html file to be preprocessed
preprocessors: {
'directory/to/html/directive.html': 'ng-html2js'
},
plugins : [
'karma-chrome-launcher',
'karma-jasmine',
'karma-ng-html2js-preprocessor' //include as a plugin too
],
ngHtml2JsPreprocessor: {
//this part has a lot of useful features but unfortunately I
//never got them to work, Google if you need help
},
directive.js
export class myDirectiveController {
constructor(/*dependencies for controller*/) {
//initializations
}
//other methods for directive class
}
export class myDirective implements ng.IDirective {
constructor(/*dependencies for directive*/) { }
static instance(/*dependencies*/): ng.IDirective {
return new myDirective(/*dependencies for directive*/);
}
restrict = 'E';
templateUrl = 'myDirective.html';
controller = myDirectiveController;
controllerAs = 'myDirectiveController';
scope: {};
}
angular
.module('myDirectiveModule')
.directive('myDirective', myDirective.instance);
myDirective.spec.js
describe("myDirective", () => {
//do you variable declarations but I'm leaving them out for simplicity
beforeEach(() => {
angular.mock.module(
'myDirectiveModule', //and other modules in use
'directory/to/html/directive.html'
//include directive html as a module
)
// now do your mock dependencies as you did with services
mockDependency = sinon.stub(new MockDependency());
angular.mock.module(($provide): void => {
$provide.value('dependency', mockDependency);
}
//inject $compile and $rootScope
inject(($compile, $rootScope) => {
scope = $rootScope.$new();
// your directive gets compiled here
element = angular.element("<my-directive></my-directive>");
$compile(element)(scope);
$rootScope.$digest();
directiveController = element.controller('myDirective'); //this is your directive's name defined in .directive("myDirective", ...)
});
}
describe("simple test", () => {
it("should click a link", () => {
var a = element.find("a");
a.triggerHandler('click');
//very important to call scope.$digest every you change anything in the view or the model
scope.$digest();
expect('whatever').toBe('whatever');
});
});
}
之前当我声明在 Angular 之前包含你的 jQuery 文件时,这样做是因为 angular.element() 会生成一个 jQuery 对象,你可以在该对象上使用 jQuery API,但如果你不首先包含 jQuery,那么你 angular.element() 将生成一个 jQuery 对象,您可以在该对象上使用 jQuery API。 element() 返回一个包含较少方法的 jQLite 对象。
调用 scope.$digest() 也很重要,因为它会更新指令的绑定。