ホームページ  >  記事  >  ウェブフロントエンド  >  Angular.Js の自動テストについて

Angular.Js の自動テストについて

不言
不言オリジナル
2018-07-02 16:03:161888ブラウズ

この記事では主にAngular.Jsの自動テストを紹介していますが、ある程度の参考値があるので、必要な方は参考にしてください

Angularプロジェクトの規模が一定レベルに達すると、テスト作業が始まります。必須。 。なぜテストを自動化するのでしょうか? 1. 出力品質を向上させます。 2. 再建時の痛みを軽減します。とにかく、私は最近たくさんのリファクタリングを行って、多くの苦痛を経験しました。 3. 新しい人が引き継ぎやすくする。次の記事では Angular.Js の自動テストについて詳しく説明していますので、必要な方は参考にしてください。

この記事は ng のテスト部分に焦点を当てており、主に次の 3 つの側面が含まれます:

  1. フレームワークの選択 (Karma+Jasmine)

  2. テストの分類と選択 (単体テスト + エンドツーエンド) testing)

  3. ngの各モジュールのテストケースの書き方

以下の部分で詳しく紹介します。

テストの分類

テストでは、一般的に単体テストとエンドツーエンドテストに分けられます。単体テストは、開発者がコードの特定の部分の妥当性を確認する技術です。 to-end (E2E) は、多数のコンポーネントが想定どおりに動作することを確認したい場合に使用します。

単体テストは、TDD (テスト駆動開発) と BDD (動作駆動開発) の 2 つのカテゴリに分類されます。

以下では 2 つの開発モードに焦点を当てます。

TDD (テスト駆動開発) は、ソフトウェア開発を推進するためにテスト ケースを使用することです。

TDD をより深く理解したい場合は、TDD を 5 つの異なる段階に分けることができます:

  1. まず、開発者はいくつかのテスト メソッドを作成します。

  2. 次に、開発者はこれらのテストを使用しますが、これらの関数のコードが実際に実行するように書かれていないため、明らかにテストは失敗します。

  3. 次に、開発者はテスト対象のコードを実装します。

  4. 開発者がコードをうまく書くと、次の段階でテストが合格することがわかります。

  5. その後、開発者は、コードをリファクタリングし、コメントを追加して、コードを整理することができます。新しく追加されたコードが何かを壊した場合、テストによってその失敗が通知されることがわかります。

フローチャートは次のとおりです:


TDD

TDDの利点:

  1. システムの最終実装コードを駆動でき、テストコードでカバーできます。つまり、「すべての行のコードはすべてテスト可能」です。

  2. テストコードはコードを実装するための正しいガイドとして機能し、最終的には正しいシステム動作に進化して、開発プロセス全体をより効率的にします。

BDD (Behavior-Driven Development) とは、コードの実装の詳細についてではなく、動作についてテストを作成する必要があることを意味します。 BDD は動作、つまりソフトウェアがどのように実行されるべきかをテストします。

  1. TDD と比較して、BDD ではソフトウェアを開発する前に、最初に動作仕様 (機能の詳細) を記述する必要があります。機能の詳細とテストは非常によく似ていますが、機能の詳細はもう少し微妙です。 BDD では、より詳細なアプローチが取られ、文章のように見えます。

  2. BDD テストは、実際の結果ではなく機能に焦点を当てる必要があります。 BDD は、TDD のようなソフトウェアのテストではなく、ソフトウェアの設計に役立つとよく聞きます。

最終要約: TDD の反復的かつ反復的な検証はアジャイル開発を保証しますが、設計に基づいてテストを生成し、テスト ケースの品質を保証する方法は明確ではなく、全員が簡潔な自然言語を使用することを BDD が推奨しています。システムの動作を説明するための概念です。この概念は、テスト ケース (つまり、システムの動作) の精度を補うものにすぎません。

テストフレームワークの選択

ngモジュールの単体テストにはkarmaとjasmineを使用します。

Karma: Node.js に基づいた JavaScript テスト実行プロセス管理ツールです。このテスト ツールの強力な機能は、ファイルの変更を監視 (監視) し、それを単独で実行し、テスト結果を表示できることです。コンソール.ログ。

ジャスミンは、js フレームワークや dom に依存しない動作駆動開発 (BDD) テスト フレームワークであり、非常にクリーンでフレンドリーな API テスト ライブラリです。

Karma

テスト ランタイム コントロール フレームワークは、chrome、filfox、phantomjs などのさまざまな環境で単体テストを実行する機能を提供します。テスト フレームワークは、jasmine、mocha、qunit をサポートし、nodejs を環境として使用する npm モジュールです。

Karma はゼロから構築されており、テスト設定の負担を軽減し、アプリケーション ロジックに集中します。ブラウザ インスタンスは、さまざまなブラウザでテストを実行するために生成され、同時にテストの実行に関するリアルタイムのフィードバックを提供し、デバッグ レポートを提供します。

テストは、テスト カバレッジ Karma カバレッジ ツール、Karman フィクスチャ ツール、Karma コーヒー処理ツールなど、いくつかの Karma プラグインにも依存します。さらに、フロントエンド コミュニティは、一般的なテストのニーズをカバーできる比較的豊富なプラグインのセットを提供します。

これは開発に関連するため、テスト関連の npm モジュールをインストールするには --save-dev パラメーターを使用することをお勧めします。一般に、karma を実行するには、次の 2 つの npm コマンドのみが必要です。フレームワークの実行には通常、karma.conf.js が必要です。一般的な例は次のとおりです。

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

実行時入力:

jasmine は、js フレームワークや DOM に依存しない、動作駆動型の開発用のテスト フレームワークです。以下は、test.js を説明するための具体的な例です。 まず、テスト ケースは定義する関数の説明で始まり、2 つのパラメーターがあり、最初のパラメーターはテストの一般的な中心的な内容を記述するために使用され、2 番目のパラメーターは実際のテスト コードを記述する関数です

単一の特定のテスト タスクを定義するために使用されます。最初のパラメーターは、テストの内容を記述するために使用されます。2 番目のパラメーターは、主に計算に使用されます。変数または式の値を取得し、それを使用して式の値を計算します。 値を比較するか、他のイベントを実行します

  1. BeforeEach と afterEach は、主にテスト タスクの実行前と後に何かを行うために使用されます。 . 上記の例は、実行前に変数の値を変更し、実行完了後に変数の値をリセットする

  2. 単体テストを開始する
  3. の4つの部分です。コントローラー、命令、フィルター、およびサービスの関連する単体テストを作成します。プロジェクトのアドレスは angular-seed (クリックしてください) プロジェクトで、デモをダウンロードしてテスト ケースを実行できます。
  4. このデモは、ボタンを押してメモ リストに新しいメモを追加できるテキスト入力ボックスを含むシンプルな Todo アプリケーションです。LocalStorage をカプセル化してメモ情報を保存します。

    まず、angular のテスト関連コンポーネント angular-mocks を紹介しましょう。

angular-mocksを理解する

Angularでは、モジュールは依存関係注入を通じてロードされ、インスタンス化されるため、モジュール定義、ロード、依存関係注入およびその他の機能を提供する公式のangular-mocks.jsテストツールが提供されています。

一般的に使用されるメソッドの一部 (ウィンドウ名前空間にマウントされている):

angular.mock.module: module は、既存のモジュールをロードし、inject メソッドによって挿入されるモジュール情報を構成するために使用されます。具体的な使用方法は次のとおりです:

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

このメソッドは通常、テストケースを実行する前にモジュール構成を取得するために beforeEach で使用されます。

angular.mock.inject: inject は、テスト ケースで呼び出すために構成された ng モジュールを挿入するために使用されます。具体的な使い方は以下の通りです:

karma start test/karma.conf.js
実際、injectはangular.injectメソッドを使って作成される組み込みの依存関係注入インスタンスであり、内部のモジュールの依存関係処理は通常のngモジュールと同じです。

Controller部分

angular.mock.module: module用来加载已有的模块,以及配置inject方法注入的模块信息。具体使用如下:

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

该方法一般在beforeEach中使用,在执行测试用例之前可以获得模块的配置。

angular.mock.inject: inject用来注入配置好的ng模块,来供测试用例里进行调用。具体使用如下:

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

其实inject里面就是利用angular.inject方法创建的一个内置的依赖注入实例,然后里面的模块和普通的ng模块的依赖处理是一样的。

Controller部分

Angular模块是todoApp,控制器是TodoController,当按钮被点击时,TodoController的createNote()函数会被调用。下面是app.js的代码部分。

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

在todoController中用了个叫做notesFactory的服务来存储和提取笔记。当createNote()被调用时,会使用这个服务将一条信息存入LocalStorage中,然后清空当前的note。因此,在编写测试模块是,应该保证控制器初始化,scope中有一定数量的笔记,在调用createNote()之后,笔记的数量应该加一。

具体的单元测试如下:

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

在beforeEach中,每一个测试用例被执行之前,都需要加载模块module("todoApp")

由于不需要外部以来,因此我们本地建立一个假的mockService来代替factory,用来模拟noteFactory,其中包含相同的函数,get()put() 。这个假的factory从数组中加载数据代替localStorage的操作。

在it中,声明了依赖项目$rootScope$controller,都可以由Angular自动注入,其中$rootScope用来获得根作用域,$controllerAngularモジュールはtodoApp、コントローラーはTodoControllerで、ボタンがクリックされるとTodoControllerのcreateNote()関数が呼び出されます。以下はapp.jsのコード部分です。

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);
 })
 );
});
🎜は、todoControllerのnotesFactoryというサービスを使用してメモを保存および取得します。 createNote() が呼び出されると、このサービスは LocalStorage に情報を保存し、現在のメモをクリアするために使用されます。したがって、テスト モジュールを作成するときは、コントローラーが初期化されていること、および createNote() を呼び出した後、スコープ内に特定の数のノートがあることを確認する必要があります。 。 🎜🎜特定の単体テストは次のとおりです: 🎜
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);
 });
});
🎜 beforeEach では、各テスト ケースが実行される前に、モジュール module("todoApp") をロードする必要があります。 🎜🎜外部依存関係が必要ないため、同じ関数 get()put() が含まれる noteFactory をシミュレートするために、ファクトリではなくローカルで偽のモックサービスを構築します。 >コード>。この偽のファクトリーは、localStorage の代わりに配列からデータをロードします。 🎜🎜その中で、依存プロジェクト $rootScope$controller が宣言されており、どちらも Angular によって自動的に挿入できます。ここで、$rootScopeはルート スコープの取得に使用され、$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中获取数据源的方式

以上がAngular.Js の自動テストについての詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。