Jasmine, Karma, Angular how to write test on my Angular app?

匿名 (未验证) 提交于 2019-12-03 01:25:01

问题:

I have just jumped to another project and, basically, I have been asked to write Unit tests. Since I have already know Protractor for e2e testing, I now switched to Karma and Jasmine to carry out unit testing. I have already downloaded karma, jasmine, karma-jasmine, and karma-chrome-launcher. I installed angular-mocks as well so I should be ready to start. I have read many things on the internet but, now, what I really need is a concrete example of a real app to figure out how to start writing tests. I don't need easy examples but concrete examples and full explenations. Books and useful links are appreciated as well. Thanks in advance for your help/time.

回答1:

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");     }); }); 

This is a simple example of how to test an angular service. In this case the service is called TestedService.

The first thing you'll see is that three variable declarations. The first two are declared to mock out the two dependencies of this service.(Assume this service has two dependencies). The last variable declaration is going to be assigned the actual service being tested.

Now in the beforeEach:

angular.mock.module 

This line registers the module in which the service you are testing is in. This line is very important.

The next two line use Sinon.js to mock the dependencies of the service being tested. I recommend looking into Sinon.js

The way it works is we have a dependency called "FirstDependency" which I created a stub of and called "MockedFirstDependency" and here I created an instance of it.

Now for the next part which (the part that includes $provide)

$provide.value('FirstDependency', mockFirstDependency); 

What the above line does is it tells Angular that every time the FirstDependency service is used, instead use mockFirstDependency.

Now in the next beforeEach all I do is inject the actual service which I am testing and assign it to my global variable.

Then let the testing begin

EDIT: Testing Controllers

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");         });     }); }); 

EDIT: Testing Directives

First off you will need to install Html2JsPreprocessor with this command: npm install karma-ng-html2js-preprocessor --save-dev as stated 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("");             $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');         });      }); } 

Earlier when I stated to included your jQuery file before you Angular, do this because angular.element() will produce a jQuery object on which you can use the jQuery API, but if you do not include jQuery first then you angular.element() returns a jQLite object which contains less methods.

It is also important to call scope.$digest() because that updates the bindings for your directive.



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!