Points de base
angular-mocks
, qui fournit des simulations pour un ensemble de services angularjs couramment utilisés. $provide
实现模拟服务来完成。 Cette dernière méthode est préférable, ce qui peut éviter d'appeler la mise en œuvre de la méthode réelle du service. $get
doit être implémentée dans le test. Si la fonction définie dans la fonction $get
n'est pas requise dans le fichier de test, il peut se voir attribuer une valeur à une fonction vide. $window
or using a global object to create values or constants and injecting them au besoin. Le concept de conception AngularJS comprend des tests. Le code source du cadre est très bien testé et tout code écrit à l'aide du cadre est également testable. Le mécanisme d'injection de dépendance intégré permet de tester chaque composant écrit dans AngularJS. Le code dans les applications AngularJS peut être testé unitaire à l'aide de tout cadre de test JavaScript existant. Le cadre le plus courant utilisé pour tester le code AngularJS est le jasmin. Tous les exemples d'extraits de code de cet article sont écrits à l'aide de Jasmine. Si vous utilisez un autre cadre de test dans votre projet angulaire, vous pouvez toujours appliquer les idées discutées dans cet article.
Cet article suppose que vous avez déjà de l'expérience dans les tests unitaires et tester le code AngularJS. Vous n'avez pas besoin d'être un expert des tests. Si vous avez une compréhension de base des tests et que vous pouvez écrire des cas de test simples pour les applications AngularJS, vous pouvez continuer à lire cet article.
Le rôle de la simulation dans les tests unitaires
La tâche de chaque test unitaire est de tester la fonctionnalité d'un morceau de code isolément. L'isolement du système testé peut parfois être difficile car les dépendances peuvent provenir de différentes sources et nous devons comprendre pleinement les responsabilités de l'objet à simuler.
Dans les langages non typés tels que JavaScript, la simulation est difficile car il n'est pas facile de comprendre la structure de l'objet à simuler. Dans le même temps, il offre également une flexibilité, c'est-à-dire pour ne simuler qu'une partie de l'objet actuellement utilisé par le système testé et ignorer le reste.
MOCK EN ANGULLARJS TEST
Étant donné que l'un des principaux objectifs d'AngularJS est la testabilité, l'équipe principale met des efforts supplémentaires pour faciliter les tests et nous fournit un ensemble de simulations dans le module angular-mocks
. Ce module contient des simulations autour d'un ensemble de services angularjs (tels que $http
, $timeout
, $animate
, etc.) qui sont largement utilisés dans toute application angularjs. Ce module réduit le temps qu'il faut aux développeurs pour rédiger des tests.
Ces simulations sont très utiles lors de la rédaction de tests pour de véritables applications commerciales. En même temps, ils ne sont pas suffisants pour tester l'ensemble de l'application. Nous devons se moquer de toutes les dépendances dans le cadre mais non moqué - les dépendances à partir de plugins tiers, d'objets globaux ou de dépendances créées dans l'application. Cet article présentera quelques conseils sur les dépendances AngularJS moqueuses.
sont le type de dépendance le plus courant dans les applications AngularJS. Comme vous le savez probablement déjà, les services sont un terme surchargé dans AngularJS. Il peut se référer à un service, une usine, une valeur, une constante ou un fournisseur. Nous discuterons du fournisseur dans la section suivante. Le service peut être simulé de l'une des manières suivantes:
$provide
pour implémenter les services de simulation. Je n'aime pas la première méthode car elle peut conduire à l'implémentation réelle de la méthode du service d'appel. Nous utiliserons la deuxième méthode pour simuler le service suivant:
angular.module('sampleServices', []) .service('util', function() { this.isNumber = function(num) { return !isNaN(num); }; this.isDate = function(date) { return (date instanceof Date); }; });
Le code d'extrait de code suivant crée une simulation du service ci-dessus:
module(function($provide) { $provide.service('util', function() { this.isNumber = jasmine.createSpy('isNumber').andCallFake(function(num) { // 模拟实现 }); this.isDate = jasmine.createSpy('isDate').andCallFake(function(num) { // 模拟实现 }); }); }); // 获取模拟服务的引用 var mockUtilSvc; inject(function(util) { mockUtilSvc = util; });
Bien que l'exemple ci-dessus utilise le jasmin pour créer des espions, vous pouvez le remplacer par sinon.js pour atteindre la fonctionnalité équivalente.
Il est préférable de créer toutes les simulations après le chargement de tous les modules requis pour le test. Sinon, si un service est défini dans un module chargé, l'implémentation réelle remplace l'implémentation simulée.
Les constantes, les usines et les valeurs peuvent être simulées séparément en utilisant $provide.constant
, $provide.factory
et $provide.value
.
Le fournisseur de simulation est similaire au service de simulation. Toutes les règles qui doivent être suivies lorsque les fournisseurs d'écriture doivent également être suivis lorsqu'ils se moquent d'eux. Considérez le fournisseur suivant:
angular.module('mockingProviders',[]) .provider('sample', function() { var registeredVals = []; this.register = function(val) { registeredVals.push(val); }; this.$get = function() { function getRegisteredVals() { return registeredVals; } return { getRegisteredVals: getRegisteredVals }; }; });
Le code d'extrait de code suivant crée une simulation pour le fournisseur ci-dessus:
module(function($provide) { $provide.provider('sample', function() { this.register = jasmine.createSpy('register'); this.$get = function() { var getRegisteredVals = jasmine.createSpy('getRegisteredVals'); return { getRegisteredVals: getRegisteredVals }; }; }); }); // 获取提供程序的引用 var sampleProviderObj; module(function(sampleProvider) { sampleProviderObj = sampleProvider; });
La différence entre obtenir des références au fournisseur et à d'autres singletons est que le fournisseur n'est pas disponible dans le bloc inject()
pour le moment, car le fournisseur est converti en usine pour le moment. Nous pouvons utiliser le bloc module()
pour obtenir leurs objets.
Dans le cas de la définition d'un fournisseur, la méthode $get
doit également être implémentée dans le test. Si vous n'avez pas besoin de la fonction définie dans la fonction $get
dans le fichier de test, vous pouvez l'affecter à une fonction vide.
Si le module à charger dans le fichier de test nécessite un tas d'autres modules, le module testé ne peut être chargé que si tous les modules requis sont chargés. Le chargement de tous ces modules provoque parfois l'échec des tests car certaines méthodes de service réelles peuvent être appelées à partir du test. Pour éviter ces difficultés, nous pouvons créer des modules virtuels pour charger les modules mesurés.
Par exemple, supposons que le code suivant représente un module avec l'exemple de service ajouté:
angular.module('sampleServices', []) .service('util', function() { this.isNumber = function(num) { return !isNaN(num); }; this.isDate = function(date) { return (date instanceof Date); }; });
Le code suivant est le bloc beforeEach
dans le fichier de test de l'exemple de service:
module(function($provide) { $provide.service('util', function() { this.isNumber = jasmine.createSpy('isNumber').andCallFake(function(num) { // 模拟实现 }); this.isDate = jasmine.createSpy('isDate').andCallFake(function(num) { // 模拟实现 }); }); }); // 获取模拟服务的引用 var mockUtilSvc; inject(function(util) { mockUtilSvc = util; });
Alternativement, nous pouvons ajouter l'implémentation simulée du service au module virtuel défini ci-dessus.
La rédaction d'une application angulaire de bout en bout peut être difficile sans utiliser la promesse. Tester des extraits de code qui s'appuient sur des méthodes qui renvoient la promesse devient un défi. Un espion du jasmin normal fait échouer certains cas de test car la fonction testée attend un objet avec la structure de promesse réelle.
Les méthodes asynchrones peuvent être simulées en utilisant une autre méthode asynchrone qui renvoie une promesse avec une valeur statique. Considérez les usines suivantes:
angular.module('mockingProviders',[]) .provider('sample', function() { var registeredVals = []; this.register = function(val) { registeredVals.push(val); }; this.$get = function() { function getRegisteredVals() { return registeredVals; } return { getRegisteredVals: getRegisteredVals }; }; });
Nous testerons la fonction getData()
dans l'usine ci-dessus. Comme nous pouvons le voir, il repose sur la méthode de service dataSourceSvc
getAllItems()
. Nous devons simuler les services et les méthodes avant de tester la fonctionnalité de la méthode getData()
.
$q
a des méthodes when()
et reject()
qui permettent à l'utilisation de valeurs statiques de résoudre ou de rejeter la promesse. Ces méthodes sont très utiles pour tester des méthodes de moquerie qui renvoient la promesse. L'extrait de code suivant simule dataSourceSvc
usine:
module(function($provide) { $provide.provider('sample', function() { this.register = jasmine.createSpy('register'); this.$get = function() { var getRegisteredVals = jasmine.createSpy('getRegisteredVals'); return { getRegisteredVals: getRegisteredVals }; }; }); }); // 获取提供程序的引用 var sampleProviderObj; module(function(sampleProvider) { sampleProviderObj = sampleProvider; });
$q
Promise termine son fonctionnement après le prochain cycle de digestion. Le cycle de digest s'exécute en continu dans l'application réelle, mais pas dans le test. Par conséquent, nous devons appeler $rootScope.$digest()
manuellement pour faire respecter la promesse. L'extrait de code suivant montre un exemple de test:
angular.module('first', ['second', 'third']) // util 和 storage 分别在 second 和 third 中定义 .service('sampleSvc', function(utilSvc, storageSvc) { // 服务实现 });
Les objets globaux proviennent des sources suivantes:
Par défaut, les objets globaux ne peuvent pas être simulés. Nous devons suivre certaines étapes pour les rendre simulatiables.
Nous ne voulons peut-être pas simuler des objets mathématiques ou des objets utilitaires (créés par la bibliothèque de sous-oreaux) car leurs opérations n'effectue aucune logique métier, exploitent l'interface utilisateur et ne communiquent pas avec la source de données. Cependant, des objets tels que $ .ajax, localstorage, Websockets, Breeze et Toastr doivent être simulés. Parce que si ces objets ne sont pas moqués, ils effectueront leurs opérations réelles lors de la réalisation de tests unitaires, ce qui peut conduire à certaines mises à jour d'interface utilisateur, des appels réseau et parfois des erreurs dans le code de test. _
En raison d'une injection de dépendance, chaque partie du code écrite en angulaire est testable. DI nous permet de passer tout objet qui suit le cale d'objet réel, juste pour que le code testé ne se casse pas lors de l'exécution. Si les objets globaux peuvent être injectés, ils peuvent être simulés. Il existe deux façons de rendre les objets globaux injectables:
$window
dans le service / contrôleur qui nécessite l'objet global et accéder à l'objet global via $window
. Par exemple, les services suivants utilisent LocalStorage via $window
: angular.module('sampleServices', []) .service('util', function() { this.isNumber = function(num) { return !isNaN(num); }; this.isDate = function(date) { return (date instanceof Date); }; });
module(function($provide) { $provide.service('util', function() { this.isNumber = jasmine.createSpy('isNumber').andCallFake(function(num) { // 模拟实现 }); this.isDate = jasmine.createSpy('isDate').andCallFake(function(num) { // 模拟实现 }); }); }); // 获取模拟服务的引用 var mockUtilSvc; inject(function(util) { mockUtilSvc = util; });
Je préfère envelopper des objets globaux avec des constantes plutôt que des valeurs, car les constantes peuvent être injectées dans des blocs de configuration ou des fournisseurs, et les constantes ne peuvent pas être décorées.
l'extrait de code suivant montre la simulation de LocalStorage et Toastr:
angular.module('mockingProviders',[]) .provider('sample', function() { var registeredVals = []; this.register = function(val) { registeredVals.push(val); }; this.$get = function() { function getRegisteredVals() { return registeredVals; } return { getRegisteredVals: getRegisteredVals }; }; });
Conclusion
La simulation est l'une des composantes importantes de l'écriture de tests unitaires dans n'importe quelle langue. Comme nous l'avons vu, l'injection de dépendance joue un rôle important dans les tests et la simulation. Le code doit être organisé de manière à ce que sa fonctionnalité puisse être facilement testée. Cet article répertorie l'ensemble d'objets le plus courant pour simuler lors du test des applications AngularJS. Le code lié à cet article peut être téléchargé à partir de GitHub.
FAQ sur les dépendances moqueuses dans les tests angularjs (FAQ)
Les dépendances moqueuses dans les tests angularjs sont un élément clé des tests unitaires. Il permet aux développeurs d'isoler le code testé et de simuler le comportement de leurs dépendances. De cette façon, vous pouvez tester comment votre code interagit avec ses dépendances sans les appeler. Ceci est particulièrement utile lorsque les dépendances sont complexes, lentes ou ont des effets secondaires que vous souhaitez éviter pendant les tests. En se moquant de ces dépendances, vous pouvez vous concentrer sur le test de la fonctionnalité de votre code dans un environnement contrôlé.
La création d'un service simulé dans AngularJS implique l'utilisation du service $provide
dans la configuration du module. Vous pouvez utiliser les méthodes $provide
du service value
, factory
ou service
pour définir une implémentation simulée d'un service. Voici un exemple de base:
module(function($provide) { $provide.provider('sample', function() { this.register = jasmine.createSpy('register'); this.$get = function() { var getRegisteredVals = jasmine.createSpy('getRegisteredVals'); return { getRegisteredVals: getRegisteredVals }; }; }); }); // 获取提供程序的引用 var sampleProviderObj; module(function(sampleProvider) { sampleProviderObj = sampleProvider; });
Dans cet exemple, nous utilisons la méthode $provide.value
pour définir l'implémentation simulée de myService
. Pendant les tests, ce service simulé sera utilisé à la place du service réel.
(Veuillez poser le reste des questions FAQ une par une en raison des limitations de l'espace, et je ferai de mon mieux pour fournir des réponses concises et claires.)
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!