Maison  >  Article  >  interface Web  >  À propos des tests automatisés d'Angular.Js

À propos des tests automatisés d'Angular.Js

不言
不言original
2018-07-02 16:03:161582parcourir

Cet article présente principalement les tests automatisés d'Angular.Js, qui ont une certaine valeur de référence. Maintenant, je le partage avec tout le monde. Les amis dans le besoin peuvent s'y référer

Lorsque l'ampleur du projet Angular atteint une certaine valeur. À un certain niveau, des travaux de test sont nécessaires. Pourquoi automatiser les tests ? 1. Améliorer la qualité de sortie. 2. Réduisez la douleur pendant la reconstruction. Quoi qu'il en soit, j'ai beaucoup refactorisé récemment et j'ai ressenti beaucoup de douleur. 3. Facilitez la prise de relais par de nouvelles personnes. L'article suivant vous donnera une introduction détaillée aux tests automatisés d'Angular.Js. Les amis dans le besoin peuvent s'y référer.

Cet article se concentre sur la partie test de ng, qui comprend principalement les trois aspects suivants :

  1. Sélection du framework (Karma+Jasmine)

  2. Classification et sélection des tests (test unitaire + test de bout en bout)

  3. Comment rédiger des cas de test pour chaque module en ng

Les parties suivantes sont présentées en détail.

Classification des tests

Dans les tests, il est généralement divisé en tests unitaires et tests unitaires de bout en bout. est d'assurer le développement Une technique utilisée pour vérifier la validité d'une certaine partie du code, de bout en bout (E2E), est utilisée lorsque vous souhaitez vous assurer qu'un ensemble de composants fonctionnent comme prévu.

Les tests unitaires sont divisés en deux catégories : TDD (Test Driven Development) et BDD (Behaviour Driven Development).

Ce qui suit se concentre sur deux modèles de développement.

TDD (Test-driven development) est l'utilisation de cas de test pour piloter le développement de votre logiciel.

Si nous voulons comprendre le TDD plus en profondeur, nous pouvons le diviser en cinq étapes différentes :

  1. Tout d'abord, le développeur écrit quelques méthodes de test .

  2. Deuxièmement, les développeurs utilisent ces tests, mais il est évident que les tests échouent car le code de ces fonctions n'a pas été écrit pour les exécuter réellement.

  3. Ensuite, le développeur implémente le code testé.

  4. Si le développeur écrit bien le code, il verra ses tests réussir à l'étape suivante.

  5. Le développeur peut alors refactoriser son code, ajouter des commentaires et le rendre propre. Le développeur sait que si le code nouvellement ajouté casse quelque chose, le test lui rappellera l'échec.

L'organigramme est le suivant :


TDD

Avantages du TDD :

  1. Il peut conduire le code d'implémentation final du système à être couvert par le code de test, c'est-à-dire que "chaque ligne de code peut être testée".

  2. Le code de test sert de guide correct pour implémenter le code et évolue finalement vers un comportement correct du système, rendant l'ensemble du processus de développement plus efficace.

BDD (Behavior-Driven Development) signifie que les tests ne doivent pas être écrits pour les détails d'implémentation du code, mais pour le comportement. BDD teste le comportement, c'est-à-dire la façon dont le logiciel doit fonctionner.

  1. Par rapport au TDD, BDD nous oblige à rédiger des spécifications comportementales (détails fonctionnels) avant de développer un logiciel. Les détails des fonctionnalités et les tests se ressemblent beaucoup, mais les détails des fonctionnalités sont un peu plus subtils. BDD adopte une approche plus détaillée et la fait ressembler à une phrase.

  2. Les tests BDD doivent se concentrer sur la fonctionnalité plutôt que sur les résultats réels. On entend souvent dire que BDD aide à concevoir des logiciels, plutôt que de tester des logiciels comme TDD.

Résumé final : La vérification itérative et répétée de TDD est la garantie d'un développement agile, mais elle ne précise pas comment générer des tests basés sur la conception et assurer la qualité des cas de test, tandis que BDD Le concept de préconiser que tout le monde utilise un langage naturel concis pour décrire le comportement du système compense simplement l'exactitude des cas de test (c'est-à-dire le comportement du système).

Sélection du cadre de test

Utilisez karma et jasmine pour les tests unitaires du module ng.

Karma : Il s'agit d'un outil de gestion du processus d'exécution de tests JavaScript basé sur Node.js. Une fonctionnalité puissante de cet outil de test est qu'il peut surveiller (observer) les modifications des fichiers, puis les exécuter lui-même. le .log de la console affiche les résultats des tests.

Jasmine est un framework de test de développement basé sur le comportement (BDD) qui ne repose sur aucun framework js ou DOM. Il s'agit d'une bibliothèque de test d'API très propre et conviviale

<.> Karma

karma est un framework de contrôle d'exécution de tests unitaires qui permet d'exécuter des tests unitaires dans différents environnements, tels que Chrome, Firfox, phantomjs, etc. Le framework de test prend en charge jasmine, mocha, qunit, oui Un module npm utilisant nodejs comme environnement.

Karma a été conçu dès le départ pour supprimer le fardeau de la configuration des tests et se concentrer sur la logique des applications. Une instance de navigateur sera générée pour exécuter des tests sur différents navigateurs. En même temps, elle pourra fournir un retour en temps réel sur l'exécution du test et fournir un rapport de débogage.

Les tests s'appuient également sur certains plug-ins Karma, tels que l'outil de couverture de test Karma-coverage, l'outil Karman-fixture et l'outil de traitement Karma-coffee. De plus, la communauté front-end fournit un ensemble relativement riche de plug-ins, qui peuvent couvrir les besoins courants en matière de tests.

Il est recommandé d'utiliser le paramètre --save-dev pour installer les modules npm liés aux tests, car cela est lié au développement. Généralement, pour exécuter karma, vous n'avez besoin que des deux commandes npm suivantes :

npm install karma --save-dev
npm install karma-junit-reporter --save-dev
Ensuite, un framework en cours d'exécution typique nécessite généralement un fichier de configuration. Dans karma, il peut s'agir d'un karma.conf.js. Le code à l'intérieur est un style nodejs. 🎜>

Exécuter lors de la saisie :

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

karma start test/karma.conf.js
jasmine

jasmine est un cadre de test de développement axé sur le comportement qui ne repose pas sur n'importe quel framework js ou dom. Une bibliothèque de tests d'API très propre et conviviale

Ce qui suit est un exemple spécifique pour illustrer 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);
 });
});
Tout d'abord. , tout scénario de test est décrit avec la fonction de description Définition, il a deux paramètres, le premier est utilisé pour décrire le contenu général de base du test, le deuxième paramètre est une fonction, qui écrit du code de test réel
  1. il est utilisé La définition d'une seule tâche de test spécifique comporte également deux paramètres. Le premier est utilisé pour décrire le contenu du test. Le deuxième paramètre est une fonction qui stocke certaines méthodes de test
  2. <.>

    expect est principalement utilisé pour calculer La valeur d'une variable ou d'une expression est ensuite utilisée pour comparer avec la valeur attendue ou effectuer d'autres événements

  3. beforeEach et afterEach sont principalement utilisé pour faire avant et après l'exécution de la tâche de test Certaines choses, l'exemple ci-dessus consiste à modifier la valeur de la variable avant l'exécution, puis à réinitialiser la valeur de la variable une fois l'exécution terminée

  4. Démarrer le test unitaire

Les tests unitaires pertinents sont rédigés en quatre parties : contrôleur, instruction, filtre et service. L'adresse du projet est le projet angulaire-seed (cliquez sur moi), vous pouvez télécharger la démo et exécuter ses cas de test. La démo est une application de tâches simple qui contient une zone de saisie de texte dans laquelle vous pouvez écrire des notes. Appuyez sur le bouton pour ajouter de nouvelles notes à la liste de notes. Notesfactory est utilisé pour encapsuler LocalStorage pour stocker les informations de note. .

Introduisez d'abord les composants angulaires liés aux tests dans angulaire.

Comprendre les simulations angulaires

Dans Angular, les modules sont chargés et instanciés via l'injection de dépendances, donc l'outil de test officiel angulaire -mocks.js pour fournir la définition du module, le chargement, l'injection de dépendances et d'autres fonctions. Certaines des méthodes couramment utilisées (montées dans l'espace de noms de la fenêtre) :

sont utilisées pour charger les modules existants et configurer les informations de module injectées par la méthode inject. L'utilisation spécifique est la suivante :

Cette méthode est généralement utilisée dans beforeEach pour obtenir la configuration du module avant d'exécuter le scénario de test. angular.mock.module: module

beforeEach(module(&#39;myApp.filters&#39;));
beforeEach(module(function($provide) {
 $provide.value(&#39;version&#39;, &#39;TEST_VER&#39;);
}));
est utilisé pour injecter le module ng configuré pour appeler dans les cas de test. L'utilisation spécifique est la suivante :

En fait, inject est une instance d'injection de dépendances intégrée créée à l'aide de la méthode angulaire.inject, et le traitement des dépendances des modules à l'intérieur est le même que celui de le module ng ordinaire. angular.mock.inject: inject

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

Partie contrôleur

Le module angulaire est todoApp, le contrôleur est TodoController, lorsque vous cliquez sur le bouton, la fonction de TodoController sera appelée. Vous trouverez ci-dessous la partie code de app.js.

Un service appelé notesFactory est utilisé dans todoController pour stocker et récupérer des notes. Lorsque createNote() est appelé, ce service sera utilisé pour stocker une information dans LocalStorage puis effacer la note actuelle. Par conséquent, lors de l'écriture d'un module de test, vous devez vous assurer que le contrôleur est initialisé et qu'il y a un certain nombre de notes dans la portée. Après avoir appelé createNote(), le nombre de notes doit être augmenté d'une.

var todoApp = angular.module(&#39;todoApp&#39;,[]);
todoApp.controller(&#39;TodoController&#39;,function($scope,notesFactory){
 $scope.notes = notesFactory.get();
 $scope.createNote = function(){
 notesFactory.put($scope.note);
 $scope.note=&#39;&#39;;
 $scope.notes = notesFactory.get();
 }
});
todoApp.factory(&#39;notesFactory&#39;,function(){
 return {
 put: function(note){ 
 localStorage.setItem(&#39;todo&#39; + (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;
 } 
 };
});
Les tests unitaires spécifiques sont les suivants :

createNote()

Dans beforeEach, avant l'exécution de chaque scénario de test, le module

doit être chargé.

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);
 })
 );
});
Comme aucune dépendance externe n'est nécessaire, nous construisons un faux mockService localement au lieu d'une usine pour simuler noteFactory, qui contient les mêmes fonctions,

et module("todoApp"). Cette fausse usine charge les données du tableau au lieu du localStorage.

Dans celui-ci, les projets dépendants get() et put() sont déclarés, qui peuvent tous deux être automatiquement injectés par Angular.

est utilisé pour obtenir la portée racine, et

est utilisé pour. créer un nouveau contrôleur. $rootScope

$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中获取数据源的方式

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn