Jasmine、Karma、Angular 如何在我的 Angular 应用程序上编写测试? [英] Jasmine, Karma, Angular how to write test on my Angular app?

查看:31
本文介绍了Jasmine、Karma、Angular 如何在我的 Angular 应用程序上编写测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚跳到另一个项目,基本上,我被要求编写单元测试.由于我已经了解了用于 e2e 测试的 Protractor,我现在切换到 Karma 和 Jasmine 进行单元测试.我已经下载了 karma、jasmine、karma-jasmine 和 karma-chrome-launcher.我也安装了 angular-mocks,所以我应该准备好开始了.我已经在互联网上阅读了很多东西,但是现在,我真正需要的是一个真实应用程序的具体示例,以弄清楚如何开始编写测试.我不需要简单的例子,而是具体的例子和完整的解释.书籍和有用的链接也很受欢迎.提前感谢您的帮助/时间.

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.

推荐答案

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.

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.

现在在 beforeEach 中:

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.

接下来的两行使用 Sinon.js 来模拟被测试服务的依赖关系.我建议查看 Sinon.js

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

它的工作方式是我们有一个名为FirstDependency"的依赖项,我创建了一个名为MockedFirstDependency"的存根,在这里我创建了它的一个实例.

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.

现在是下一部分(包括 $provide 的部分)

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

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

上面这行代码的作用是告诉 Angular 每次使用 FirstDependency 服务时,都使用 mockFirstDependency.

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

现在在下一个 beforeEach 中,我所做的就是注入我正在测试的实际服务并将其分配给我的全局变量.

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

然后开始测试

测试控制器

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 如前所述 此处.

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("<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() 返回一个包含较少方法的 jQLite 对象.

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.

调用 scope.$digest() 也很重要,因为它会更新指令的绑定.

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

这篇关于Jasmine、Karma、Angular 如何在我的 Angular 应用程序上编写测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆