Home  >  Article  >  Web Front-end  >  About automated testing of Angular.Js

About automated testing of Angular.Js

不言
不言Original
2018-07-02 16:03:161888browse

This article mainly introduces the automated testing of Angular.Js, which has certain reference value. Now I share it with everyone. Friends in need can refer to it

When the scale of the Angular project reaches a certain level , testing work is required. Why automate testing? 1. Improve output quality. 2. Reduce the pain during reconstruction. Anyway, I have refactored a lot recently and experienced a lot of pain. 3. Make it easier for new people to take over. The following article will give you a detailed introduction to the automated testing of Angular.Js. Friends in need can refer to it.

This article focuses on the testing part of ng, which mainly includes the following three aspects:

  1. Selection of framework (Karma Jasmine)

  2. Classification and selection of tests (unit test end-to-end test)

  3. How to write test cases for each module in ng

The following parts are introduced in detail.

Classification of tests

In testing, it is generally divided into unit testing and end-to-end testing. Unit testing is to ensure that development A technique used to verify the validity of a certain part of the code, end-to-end (E2E) is used when you want to ensure that a bunch of components run as expected.

Unit testing is divided into two categories: TDD (test-driven development) and BDD (behavior-driven development).

The following focuses on two development models.

TDD (Test-driven development) is the use of test cases to drive your software development.

If we want to understand TDD more deeply, we can divide it into five different stages:

  1. First, the developer writes some test methods .

  2. Secondly, developers use these tests, but it is obvious that the tests fail because the code for these functions has not been written to actually execute them.

  3. Next, the developer implements the code under test.

  4. If the developer writes good code, he will see his tests pass in the next stage.

  5. The developer can then refactor his code, add comments, and make it tidy. The developer knows that if the newly added code breaks something, the test will remind him of the failure. .

The flow chart is as follows:


TDD

The benefits of TDD:

  1. The final implementation code that can drive the system can be covered by the test code, that is, "every line of code can be tested."

  2. Test code serves as the correct guide to implement the code and eventually evolves into correct system behavior, making the entire development process more efficient.

BDD (Behavior-Driven Development) means that tests should not be written for the implementation details of the code, but for behavior. BDD tests behavior, that is, how the software should run.

  1. Compared with TDD, BDD requires us to write behavioral specifications (functional details) first before developing software. Feature details and tests look very similar, but feature details are a little more subtle. BDD takes a more detailed approach and makes it look like a sentence.

  2. BDD testing should focus on functionality rather than actual results. You often hear that BDD helps design software, rather than testing software like TDD.

Final summary: TDD’s iterative and repeated verification is the guarantee of agile development, but it does not clarify how to generate tests based on the design and ensure the quality of test cases, while BDD The concept of advocating that everyone use concise natural language to describe system behavior just makes up for the accuracy of test cases (ie, system behavior).

Test framework selection

Use karma and jasmine to perform unit testing of the ng module.

Karma: It is a JavaScript test execution process management tool based on Node.js. A powerful feature of this testing tool is that it can monitor (Watch) file changes and then execute it by itself, through the console .log displays test results.

jasmine is a behavior-driven development (BDD) testing framework that does not rely on any js framework or dom. It is a very clean and friendly API testing library.

Karma

karma is a unit test run control framework that provides different environments to run unit tests, such as chrome, firfox, phantomjs, etc. The test framework supports jasmine, mocha, qunit, yes An npm module using nodejs as the environment.

Karma was built from the ground up to remove the burden of setting up tests and focus on application logic. A browser instance will be generated to run tests on different browsers. At the same time, it can provide real-time feedback on the running of the test and provide a debug report.

Testing also relies on some Karma plug-ins, such as test coverage Karma-coverage tool, Karman-fixture tool and Karma-coffee processing tool. In addition, the front-end community provides a relatively rich set of plug-ins, which can cover common testing needs.

It is recommended to use the --save-dev parameter to install test-related npm modules, because this is related to development. Generally, to run karma, you only need the following two npm commands:

npm install karma --save-dev
npm install karma-junit-reporter --save-dev

Then one A typical running framework usually requires a configuration file. In karma, it can be a karma.conf.js. The code inside is a nodejs style. A common example is as follows:

module.exports = function(config){
 config.set({
 // 下面files里的基础目录
 basePath : '../',
 // 测试环境需要加载的JS信息
 files : [
 'app/bower_components/angular/angular.js',
 'app/bower_components/angular-route/angular-route.js',
 'app/bower_components/angular-mocks/angular-mocks.js',
 'app/js/**/*.js',
 'test/unit/**/*.js'
 ],
 // 是否自动监听上面文件的改变自动运行测试
 autoWatch : true,
 // 应用的测试框架
 frameworks: ['jasmine'],
 // 用什么环境测试代码,这里是chrome`
 browsers : ['Chrome'],
 // 用到的插件,比如chrome浏览器与jasmine插件
 plugins : [
  'karma-chrome-launcher',
  'karma-firefox-launcher',
  'karma-jasmine',
  'karma-junit-reporter'
  ],
 // 测试内容的输出以及导出用的模块名
 reporters: ['progress', 'junit'],
 // 设置输出测试内容文件的信息
 junitReporter : {
 outputFile: 'test_out/unit.xml',
 suite: 'unit'
 }
 });
};

Runtime input:

karma start test/karma.conf.js

jasmine

jasmine is a testing framework for behavior-driven development. It does not rely on any js framework or dom. It is a very clean and friendly API. Test library.

The following is a specific example to illustrate test.js:

describe("A spec (with setup and tear-down)", function() {
 var foo;
 beforeEach(function() {
 foo = 0;
 foo += 1;
 });
 afterEach(function() {
 foo = 0;
 });
 it("is just a function, so it can contain any code", function() {
 expect(foo).toEqual(1);
 });
 it("can have more than one expectation", function() {
 expect(foo).toEqual(1);
 expect(true).toEqual(true);
 });
});
  1. First, any test case is defined with the describe function, which has two parameters. One is used to describe the general central content of the test. The second parameter is a function in which some real test code is written

  2. It is used to define a single specific test task. There are also two Parameters, the first one is used to describe the test content, the second parameter is a function, which stores some test methods

  3. expect is mainly used to calculate the value of a variable or an expression , and then used to compare with the expected value or do some other events

  4. beforeEach and afterEach are mainly used to do something before and after executing the test task. The above example is Change the value of the variable before execution, and then reset the value of the variable after execution is complete

Start unit test

The following unit tests are written in four parts: controller, instruction, filter and service. The project address is the angular-seed (click me) project, you can download the demo and run its test cases.

The demo is a simple todo application that contains a text input box in which you can write some notes. Press the button to add new notes to the note list. Notesfactory is used to encapsulate LocalStorage to store note information. .

First introduce the testing-related components angular-mocks in angular.

Understand angular-mocks

In Angular, modules are loaded and instantiated through dependency injection, so the official angular -mocks.js testing tool to provide module definition, loading, dependency injection and other functions.

Some of the commonly used methods (mounted in the window namespace):

angular.mock.module: module is used to load existing modules and configure Module information injected by the inject method. The specific usage is as follows:

beforeEach(module('myApp.filters'));
beforeEach(module(function($provide) {
 $provide.value('version', 'TEST_VER');
}));

This method is generally used in beforeEach to obtain the module configuration before executing the test case.

angular.mock.inject: inject is used to inject the configured ng module for calling in test cases. The specific usage is as follows:

it('should provide a version', inject(function(mode, version) {
 expect(version).toEqual('v1.0.1');
 expect(mode).toEqual('app');
 }));

In fact, inject is a built-in dependency injection instance created using the angular.inject method, and the dependency processing of the modules inside is the same as that of the ordinary ng module.

Controller part

Angular module is todoApp, controller is TodoController, when the button is clicked, TodoController's createNote() The function will be called. Below is the code part of app.js.

var todoApp = angular.module('todoApp',[]);
todoApp.controller('TodoController',function($scope,notesFactory){
 $scope.notes = notesFactory.get();
 $scope.createNote = function(){
 notesFactory.put($scope.note);
 $scope.note='';
 $scope.notes = notesFactory.get();
 }
});
todoApp.factory('notesFactory',function(){
 return {
 put: function(note){ 
 localStorage.setItem('todo' + (Object.keys(localStorage).length + 1), note);
 },
 get: function(){
 var notes = [];
 var keys = Object.keys(localStorage);
 for(var i = 0; i < keys.length; i++){
  notes.push(localStorage.getItem(keys[i]));
 }
 return notes;
 } 
 };
});

A service called notesFactory is used in todoController to store and retrieve notes. When createNote() is called, this service will be used to store a piece of information in LocalStorage and then clear the current note. Therefore, when writing a test module, you should ensure that the controller is initialized and there is a certain number of notes in the scope. After calling createNote(), the number of notes should be increased by one.

The specific unit test is as follows:

describe(&#39;TodoController Test&#39;, function() {
 beforeEach(module(&#39;todoApp&#39;)); // 将会在所有的it()之前运行
 // 我们在这里不需要真正的factory。因此我们使用一个假的factory。
 var mockService = {
 notes: [&#39;note1&#39;, &#39;note2&#39;], //仅仅初始化两个项目
 get: function() {
 return this.notes;
 },
 put: function(content) {
 this.notes.push(content);
 }
 };
 // 现在是真正的东西,测试spec
 it(&#39;should return notes array with two elements initially and then add one&#39;,
 inject(function($rootScope, $controller) { //注入依赖项目
 var scope = $rootScope.$new();
 // 在创建控制器的时候,我们也要注入依赖项目
 var ctrl = $controller(&#39;TodoController&#39;, {$scope: scope, notesFactory:mockService});
 // 初始化的技术应该是2
 expect(scope.notes.length).toBe(2);
 // 输入一个新项目
 scope.note = &#39;test3&#39;;
 // now run the function that adds a new note (the result of hitting the button in HTML)
 // 现在运行这个函数,它将会增加一个新的笔记项目
 scope.createNote();
 // 期待现在的笔记数目是3
 expect(scope.notes.length).toBe(3);
 })
 );
});

In beforeEach, before each test case is executed, the module module("todoApp") needs to be loaded.

Since no external dependency is needed, we create a fake mockService locally instead of factory to simulate noteFactory, which contains the same functions, get() and put( ) . This fake factory loads data from the array instead of localStorage.

In it, the dependent projects $rootScope and $controller are declared, which can be automatically injected by Angular, among which $rootScope is used Get the root scope, $controller is used to create a new controller.

$controller服务需要两个参数。第一个参数是将要创建的控制器的名称。第二个参数是一个代表控制器依赖项目的对象,
$rootScope.$new()方法将会返回一个新的作用域,它用来注入控制器。同时我们传入mockService作为假factory。
之后,初始化会根据notes数组的长度预测笔记的数量,同时在执行了createNote()函数之后,会改变数组的长度,因此可以写出两个测试用例。

Factory部分

factory部分的单元测试代码如下:

describe(&#39;notesFactory tests&#39;, function() {
 var factory;
 // 在所有it()函数之前运行
 beforeEach(function() {
 // 载入模块
 module(&#39;todoApp&#39;);
 // 注入你的factory服务
 inject(function(notesFactory) {
 factory = notesFactory;
 });
 var store = {
 todo1: &#39;test1&#39;,
 todo2: &#39;test2&#39;,
 todo3: &#39;test3&#39;
 };
 spyOn(localStorage, &#39;getItem&#39;).andCallFake(function(key) {
 return store[key];
 });
 spyOn(localStorage, &#39;setItem&#39;).andCallFake(function(key, value) {
 return store[key] = value + &#39;&#39;;
 });
 spyOn(localStorage, &#39;clear&#39;).andCallFake(function() {
 store = {};
 });
 spyOn(Object, &#39;keys&#39;).andCallFake(function(value) {
 var keys=[];
 for(var key in store) {
 keys.push(key);
 }
 return keys;
 });
 });
 // 检查是否有我们想要的函数
 it(&#39;should have a get function&#39;, function() {
 expect(angular.isFunction(factory.get)).toBe(true);
 expect(angular.isFunction(factory.put)).toBe(true);
 });
 // 检查是否返回3条记录
 it(&#39;should return three todo notes initially&#39;, function() {
 var result = factory.get();
 expect(result.length).toBe(3);
 });
 // 检查是否添加了一条新纪录
 it(&#39;should return four todo notes after adding one more&#39;, function() {
 factory.put(&#39;Angular is awesome&#39;);
 var result = factory.get();
 expect(result.length).toBe(4);
 });
});

在TodoController模块中,实际上的factory会调用localStorage来存储和提取笔记的项目,但由于我们单元测试中,不需要依赖外部服务去获取和存储数据,因此我们要对localStorage.getItem()localStorage.setItem()进行spy操作,也就是利用假函数来代替这两个部分。

spyOn(localStorage,'setItem')andCallFake()是用来用假函数进行监听的。第一个参数指定需要监听的对象,第二个参数指定需要监听的函数,然后andCallfake这个API可以编写自己的函数。因此,测试中完成了对localStorage和Object的改写,使函数可以返回我们自己数组中的值。

在测试用例中,首先检测新封装的factory函数是否包含了get()put()这两个方法,,然后进行factory.put()操作后断言笔记的数量。

Filter部分

我们添加一个过滤器。truncate的作用是如果传入字符串过长后截取前10位。源码如下:

todoApp.filter(&#39;truncate&#39;,function(){
 return function(input,length){
 return (input.length > length ? input.substring(0,length) : input);
 }
});

所以在单元测试中,可以根据传入字符串的情况断言生成子串的长度。

describe(&#39;filter test&#39;,function(){
 beforeEach(module(&#39;todoApp&#39;));
 it(&#39;should truncate the input to 1o characters&#39;,inject(function(truncateFilter){
 expect(truncateFilter(&#39;abcdefghijkl&#39;,10).length).toBe(10);
 });
 );
});

之前已经对断言进行讨论了,值得注意的一点是我们需要在调用过滤器的时候在名称后面加入Filter,然后正常调用即可。

Directive部分

源码中的指令部分:

todoApp.directive(&#39;customColor&#39;, function() {
 return {
 restrict: &#39;A&#39;,
 link: function(scope, elem, attrs) {
 elem.css({&#39;background-color&#39;: attrs.customColor});
 }
 };
});

由于指令必须编译之后才能生成相关的模板,因此我们要引入$compile服务来完成实际的编译,然后再测试我们想要进行测试的元素。

angular.element()会创建一个jqLite元素,然后我们将其编译到一个新生成的自作用域中,就可以被测试了。具体测试用例如下:

describe(&#39;directive tests&#39;,function(){
 beforeEach(module(&#39;todoApp&#39;));
 it(&#39;should set background to rgb(128, 128, 128)&#39;,
 inject(function($compile,$rootScope) {
 scope = $rootScope.$new();
 // 获得一个元素
 elem = angular.element("<span custom-color=\"rgb(128, 128, 128)\">sample</span>");
 // 创建一个新的自作用域
 scope = $rootScope.$new();
 // 最后编译HTML
 $compile(elem)(scope);
 // 希望元素的背景色和我们所想的一样
 expect(elem.css("background-color")).toEqual(&#39;rgb(128, 128, 128)&#39;);
 })
 );
});

开始端到端测试

在端到端测试中,我们需要从用户的角度出发,来进行黑盒测试,因此会涉及到一些DOM操作。将一对组件组合起来然后检查是否如预想的结果一样。
在这个demo中,我们模拟用户输入信息并按下按钮的过程,检测信息能否被添加到localStorage中。

在E2E测试中,需要引入angular-scenario这个文件,并且建立一个html作为运行report的展示,在html中包含带有e2e测试代码的执行js文件,在编写完测试之后,运行该html文件查看结果。具体的e2e代码如下:

describe(&#39;my app&#39;, function() {
 beforeEach(function() {
 browser().navigateTo(&#39;../../app/notes.html&#39;);
 });
 var oldCount = -1;
 it("entering note and performing click", function() {
 element(&#39;ul&#39;).query(function($el, done) {
 oldCount = $el.children().length;
 done();
 });
 input(&#39;note&#39;).enter(&#39;test data&#39;);
 element(&#39;button&#39;).query(function($el, done) {
 $el.click();
 done();
 });
 });
 it(&#39;should add one more element now&#39;, function() {
 expect(repeater(&#39;ul li&#39;).count()).toBe(oldCount + 1);
 }); 
});

我们在端到端测试过程中,首先导航到我们的主html页面app/notes.html,可以通过browser.navigateTo()来完成,element.query()函数选择了ul元素并记录其中有多少个初始化的项目,存放在oldCount变量中。

然后通过input('note').enter()来键入一个新的笔记,然后模拟一下点击操作来检查是否增加了一个新的笔记(li元素)。然后通过断言可以将新旧的笔记数进行对比。

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

关于AJax与Jsonp跨域访问的问题

三种AngularJS中获取数据源的方式

The above is the detailed content of About automated testing of Angular.Js. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn