Heim >Web-Frontend >js-Tutorial >Über automatisierte Tests von Angular.Js

Über automatisierte Tests von Angular.Js

不言
不言Original
2018-07-02 16:03:161917Durchsuche

Dieser Artikel stellt hauptsächlich das automatisierte Testen von Angular.Js vor, das einen gewissen Referenzwert hat. Jetzt kann ich es mit allen Freunden teilen, die es brauchen.

Wenn der Umfang des Angular-Projekts erreicht ist Auf einem bestimmten Niveau sind Testarbeiten erforderlich. Warum Tests automatisieren? 1. Verbessern Sie die Ausgabequalität. 2. Reduzieren Sie die Schmerzen während der Rekonstruktion. Wie auch immer, ich habe in letzter Zeit viel umgestaltet und dabei große Schmerzen gehabt. 3. Erleichtern Sie die Übernahme durch neue Leute. Der folgende Artikel gibt Ihnen eine detaillierte Einführung in das automatisierte Testen von Angular.Js. Freunde in Not können darauf zurückgreifen.

Dieser Artikel konzentriert sich auf den Testteil von ng, der hauptsächlich die folgenden drei Aspekte umfasst:

  1. Framework-Auswahl (Karma+Jasmine)

  2. Testklassifizierung und -auswahl (Unit-Test + End-to-End-Test)

  3. So schreiben Sie Testfälle für jedes Modul in ng

Die folgenden Teile werden im Detail vorgestellt.

Klassifizierung von Tests

Beim Testen wird im Allgemeinen in Unit-Tests und End-to-End-Unit-Tests unterteilt dient der Sicherstellung der Entwicklung. Eine Technik zur Überprüfung der Gültigkeit eines bestimmten Teils des Codes. End-to-End (E2E) wird verwendet, wenn Sie sicherstellen möchten, dass eine Reihe von Komponenten wie erwartet ausgeführt werden.

Unit-Tests sind in zwei Kategorien unterteilt: TDD (Test Driven Development) und BDD (Behavior Driven Development).

Im Folgenden konzentrieren wir uns auf zwei Entwicklungsmodelle.

TDD (Testgetriebene Entwicklung) ist die Verwendung von Testfällen, um Ihre Softwareentwicklung voranzutreiben.

Wenn wir TDD tiefer verstehen wollen, können wir es in fünf verschiedene Phasen unterteilen:

  1. Zuerst schreibt der Entwickler einige Testmethoden.

  2. Zweitens verwenden Entwickler diese Tests, aber es ist offensichtlich, dass die Tests fehlschlagen, weil der Code für diese Funktionen nicht geschrieben wurde, um sie tatsächlich auszuführen.

  3. Als nächstes implementiert der Entwickler den zu testenden Code.

  4. Wenn der Entwickler den Code gut schreibt, werden seine Tests in der nächsten Phase erfolgreich sein.

  5. Der Entwickler kann dann seinen Code umgestalten, Kommentare hinzufügen und ihn aufräumen. Der Entwickler weiß, dass der Test ihn an den Fehler erinnern wird.

Das Flussdiagramm sieht wie folgt aus:


TDD

Vorteile von TDD:

  1. Es kann dazu führen, dass der endgültige Implementierungscode des Systems vom Testcode abgedeckt wird, dh „jede Codezeile kann getestet werden“.

  2. Testcode dient als korrekter Leitfaden für die Implementierung des Codes und führt letztendlich zu korrektem Systemverhalten, wodurch der gesamte Entwicklungsprozess effizienter wird.

BDD (Behavior-Driven Development) bedeutet, dass Tests nicht für die Implementierungsdetails des Codes geschrieben werden sollten, sondern für das Verhalten. BDD testet das Verhalten, also wie die Software laufen soll.

  1. Im Vergleich zu TDD erfordert BDD, dass wir zunächst Verhaltensspezifikationen (funktionale Details) schreiben, bevor wir Software entwickeln. Funktionsdetails und Tests sehen sehr ähnlich aus, die Funktionsdetails sind jedoch etwas subtiler. BDD geht detaillierter vor und lässt es wie einen Satz aussehen.

  2. BDD-Tests sollten sich auf die Funktionalität und nicht auf tatsächliche Ergebnisse konzentrieren. Man hört oft, dass BDD beim Entwerfen von Software hilft und nicht beim Testen von Software wie TDD.

Abschließende Zusammenfassung: Die iterative und wiederholte Überprüfung von TDD ist die Garantie für agile Entwicklung, klärt jedoch nicht, wie Tests basierend auf dem Design generiert und die Qualität sichergestellt werden von Testfällen, während BDD das Konzept, dafür zu plädieren, dass jeder eine prägnante natürliche Sprache zur Beschreibung des Systemverhaltens verwendet, nur die Genauigkeit von Testfällen (dh Systemverhalten) ausgleicht.

Auswahl des Testframeworks

Verwenden Sie Karma und Jasmin, um Unit-Tests des NG-Moduls durchzuführen.

Karma: Es handelt sich um ein auf Node.js basierendes JavaScript-Testausführungsprozess-Management-Tool. Eine leistungsstarke Funktion dieses Testtools besteht darin, dass es Dateiänderungen überwachen (beobachten) und diese dann selbst ausführen kann Das Konsolenprotokoll zeigt Testergebnisse an.

Jasmine ist ein verhaltensgesteuertes Entwicklungs-Testframework (BDD), das nicht auf einem JS-Framework oder DOM basiert. Es ist eine sehr saubere und benutzerfreundliche API-Testbibliothek 🎜>Karma

Karma ist ein Unit-Testlauf-Steuerungsframework, das die Ausführung von Unittests in verschiedenen Umgebungen wie Chrome, Firfox, PhantomJS usw. ermöglicht. Das Testframework unterstützt Jasmin, Mocha qunit, ja Ein npm-Modul, das NodeJS als Umgebung verwendet.

Karma wurde von Grund auf entwickelt, um die Belastung durch das Einrichten von Tests zu beseitigen und sich auf die Anwendungslogik zu konzentrieren. Es wird eine Browserinstanz generiert, um Tests auf verschiedenen Browsern auszuführen. Gleichzeitig kann sie Echtzeit-Feedback zur Ausführung des Tests geben und einen Debug-Bericht bereitstellen.

Tests basieren auch auf einigen Karma-Plug-Ins, wie z. B. dem Test Coverage Karma-Coverage Tool, dem Karman-Fixture Tool und dem Karma-Coffee Processing Tool. Darüber hinaus bietet die Front-End-Community einen relativ umfangreichen Satz an Plug-Ins, die allgemeine Testanforderungen abdecken können.

Es wird empfohlen, den Parameter --save-dev zu verwenden, um testbezogene NPM-Module zu installieren, da dies mit der Entwicklung zusammenhängt. Im Allgemeinen benötigen Sie zum Ausführen von Karma nur die folgenden zwei NPM-Befehle:

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

und dann eins. Ein typisches laufendes Framework erfordert normalerweise eine Konfigurationsdatei in karma.conf.js. Der darin enthaltene Code ist wie folgt:

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

Laufzeiteingabe:

karma start test/karma.conf.js

Jasmine

Jasmine ist ein verhaltensgesteuertes Entwicklungstest-Framework, das nicht auf einem JS-Framework oder basiert dom. Es ist eine sehr saubere und benutzerfreundliche API-Testbibliothek

Das Folgende ist ein konkretes Beispiel zur Veranschaulichung von 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. Erstens ist es jeder Testfall Definiert mit der Funktion „beschreiben“, die über zwei Parameter verfügt. Der erste dient zur Beschreibung des allgemeinen zentralen Inhalts des Tests. Der zweite Parameter ist eine Funktion, in die echter Testcode geschrieben wird

  2. Es wird verwendet, um eine einzelne spezifische Testaufgabe zu definieren. Es gibt auch zwei Parameter, der erste wird zur Beschreibung des Testinhalts verwendet, der zweite Parameter ist eine Funktion, die einige Testmethoden speichert

  3. Expect wird hauptsächlich zum Berechnen des Werts einer Variablen oder eines Ausdrucks verwendet und dann zum Vergleichen mit dem erwarteten Wert oder zum Ausführen einiger anderer Ereignisse.

  4. BeforeEach und AfterEach sind hauptsächlich Wird verwendet, um vor und nach der Ausführung der Testaufgabe etwas zu tun. Das obige Beispiel ist: Ändern Sie den Wert der Variablen vor der Ausführung und setzen Sie den Wert der Variablen nach Abschluss der Ausführung zurück

Einheitentest starten

Die relevanten Komponententests sind in den folgenden vier Teilen geschrieben: Controller, Anweisung, Filter und Service. Die Projektadresse ist das Angular-Seed-Projekt (klicken Sie auf mich). Sie können die Demo herunterladen und die Testfälle ausführen.

Die Demo ist eine einfache ToDo-Anwendung, die ein Texteingabefeld enthält, in das Sie einige Notizen schreiben können. Klicken Sie auf die Schaltfläche, um neue Notizen zur Notizliste hinzuzufügen. Notesfactory wird verwendet, um LocalStorage zum Speichern von Notizinformationen zu kapseln. .

Führen Sie zunächst die testbezogenen Komponenten Angular-Mocks in Angular ein.

Angular-Mocks verstehen

In Angular werden Module durch Abhängigkeitsinjektion geladen und instanziiert, also das offizielle Angular-Mocks.js-Testtool Bereitstellung von Moduldefinition, Laden, Abhängigkeitsinjektion und anderen Funktionen.

Einige der häufig verwendeten Methoden (im Fenster-Namespace bereitgestellt):

angular.mock.module: module werden zum Laden vorhandener Module und zum Konfigurieren von Modulinformationen verwendet, die von der Injektionsmethode eingefügt werden. Die spezifische Verwendung ist wie folgt:

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

Diese Methode wird im Allgemeinen in beforeEach verwendet, um die Modulkonfiguration vor der Ausführung des Testfalls abzurufen.

angular.mock.inject: inject wird verwendet, um das konfigurierte NG-Modul zum Aufrufen von Testfällen einzuschleusen. Die spezifische Verwendung lautet wie folgt:

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

Inject ist tatsächlich eine integrierte Abhängigkeitsinjektionsinstanz, die mit der Methode „angular.inject“ erstellt wurde, und die Abhängigkeitsverarbeitung der darin enthaltenen Module ist dieselbe wie bei gewöhnlichen Modulen ng-Modul.

Controller-Teil

Winkelmodul ist todoApp, Controller ist TodoController. Wenn auf die Schaltfläche geklickt wird, wird die Funktion createNote() von TodoController aufgerufen. Unten ist der Codeteil von 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;
 } 
 };
});

verwendet einen Dienst namens „notesFactory“ in todoController, um Notizen zu speichern und abzurufen. Wenn createNote() aufgerufen wird, wird dieser Dienst verwendet, um eine Information in LocalStorage zu speichern und dann die aktuelle Notiz zu löschen. Daher sollten Sie beim Schreiben eines Testmoduls sicherstellen, dass der Controller initialisiert ist und eine bestimmte Anzahl von Notizen im Gültigkeitsbereich vorhanden ist. Nach dem Aufruf von createNote() sollte die Anzahl der Notizen um eins erhöht werden.

Der spezifische Komponententest lautet wie folgt:

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 muss vor der Ausführung jedes Testfalls das Modul module("todoApp") geladen werden.

Da keine externe Abhängigkeit erforderlich ist, erstellen wir lokal anstelle der Fabrik einen gefälschten MockService, um noteFactory zu simulieren, der die gleichen Funktionen get() und put() enthält. Diese gefälschte Fabrik lädt Daten aus dem Array statt aus localStorage.

Darin werden die abhängigen Projekte $rootScope und $controller deklariert, die beide automatisch von Angular injiziert werden können. $rootScope wird verwendet, um den Root-Bereich zu erhalten, und $controller wird dazu verwendet Erstellen Sie einen neuen 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中获取数据源的方式

Das obige ist der detaillierte Inhalt vonÜber automatisierte Tests von Angular.Js. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn