TDD est un processus de développement itératif où chaque itération commence par l'écriture d'un test qui fait partie de la spécification que nous implémentons. Les itérations courtes permettent un retour plus instantané sur le code que nous écrivons, et les mauvaises décisions de conception sont plus faciles à détecter. En écrivant les tests avant tout code de production, une bonne couverture de tests unitaires est fournie avec le territoire, mais ce n'est qu'un effet secondaire bienvenu..
Tutoriel republiéToutes les quelques semaines, nous revoyons certains des articles préférés de nos lecteurs tout au long de l'histoire du site. Ce tutoriel a été publié pour la première fois en novembre 2010.
En programmation traditionnelle, les problèmes sont résolus par programmation jusqu'à ce qu'un concept soit entièrement représenté dans le code. Idéalement, le code suit des considérations générales de conception architecturale, bien que dans de nombreux cas, peut-être surtout dans le monde de JavaScript, ce ne soit pas le cas. Ce style de programmation résout les problèmes en déterminant quel code est nécessaire pour les résoudre, une stratégie qui peut facilement conduire à des solutions saturées et étroitement couplées. S'il n'y a pas non plus de tests unitaires, les solutions produites avec cette approche peuvent même contenir du code qui n'est jamais exécuté, comme une logique de traitement des erreurs et un traitement "flexible" des arguments, ou des cas marginaux qui n'ont pas été complètement testés, si testé du tout.
Le développement piloté par les tests met le cycle de développement à l'envers. Plutôt que de se focaliser sur le code requis pour résoudre un problème, le développement piloté par les tests commence par définir l'objectif. Les tests unitaires constituent à la fois la spécification et la documentation pour les actions prises en charge et comptabilisées. Certes, l'objectif de TDD n'est pas de tester et il n'y a donc aucune garantie qu'il gère par exemple. cas de pointe mieux. Toutefois, comme chaque ligne de code est testée à l'aide d'un échantillon représentatif, TDD produira probablement moins de code en excès et les fonctionnalités prises en compte seront probablement plus robustes. Le développement proprement par test garantit que le système ne contiendra jamais de code qui n'est pas exécuté.
Le processus de développement piloté par les tests est un processus itératif dans lequel chaque itération comprend les quatre étapes suivantes:
À chaque itération, le test est la spécification. Une fois que suffisamment de code de production (et pas plus) a été écrit pour réussir le test, nous avons terminé, et nous pouvons le refactoriser pour supprimer les doublons et / ou améliorer la conception, tant que les tests réussissent toujours..
Le modèle Observer (également appelé Publish / Subscribe, ou simplement pubsub
) est un modèle de conception qui nous permet d’observer l’état d’un objet et d’être averti de son évolution. Le motif peut fournir aux objets des points d’extension puissants tout en maintenant un couplage lâche.
Il existe deux rôles dans The Observer: observable et observateur. L'observateur est un objet ou une fonction qui sera averti lorsque l'état de l'observable change. L'observable décide quand mettre à jour ses observateurs et quelles données leur fournir. L'observable fournit généralement au moins deux méthodes publiques: pubsub
, qui informe ses observateurs de nouvelles données, et pubsub
qui souscrit des observateurs aux événements.
Le développement piloté par les tests nous permet de progresser par petites étapes en cas de besoin. Dans ce premier exemple concret, nous allons commencer par la plus petite des étapes. Au fur et à mesure que nous aurons confiance dans notre code et dans le processus, nous augmenterons progressivement la taille de nos étapes lorsque les circonstances le permettront (c'est-à-dire que le code à implémenter est suffisamment simple). Écrire du code dans de petites itérations fréquentes nous aidera à concevoir notre API pièce par pièce et à nous permettre de faire moins d'erreurs. Lorsque des erreurs se produisent, nous pourrons les corriger rapidement car il sera facile de les localiser lorsque nous exécuterons des tests chaque fois que nous ajouterons quelques lignes de code..
Cet exemple utilise JsTestDriver pour exécuter des tests. Un guide d'installation est disponible sur le site web officiel.
La disposition initiale du projet se présente comme suit:
chris @ laptop: ~ / projects / observable $ tree. | - jsTestDriver.conf | - src | '- observable.js' - test '- observable_test.js
Le fichier de configuration est juste le minimum JsTestDriver
configuration:
serveur: http: // localhost: 4224 charge: - lib / *. js - test / *. js
Nous lancerons le projet en mettant en place un moyen d'ajouter des observateurs à un objet. Cela nous mènera à travers le premier test, en le regardant échouer, en le passant de la manière la plus sale possible et en le transformant finalement en quelque chose de plus raisonnable..
Le premier test tentera d’ajouter un observateur en appelant le addObserver
méthode. Pour vérifier que cela fonctionne, nous allons être francs et supposer que observable stocke ses observateurs dans un tableau et vérifier que l'observateur est le seul élément de ce tableau. Le test appartient à test / observable_test.js
et ressemble à ce qui suit:
TestCase ("ObservableAddObserverTest", "le test doit stocker une fonction": function () var observable = new tddjs.Observable (); var observer = function () ; observable.addObserver (observateur); assertEquals (observateur. observateurs [0]););
À première vue, le résultat de notre tout premier test est dévastateur:
Total 1 tests (réussi: 0; échec: 0; erreurs: 1) (0.00 ms) Firefox 3.6.12 Linux: exécuter 1 tests (réussi: 0; échecs: 0; erreurs 1) (0.00 ms) ObservableAddObserverTest.test devrait stocker erreur de fonction (0,00 ms): \ tddjs n'est pas défini /test/observable_test.js:3 Échec des tests.
Ne crains pas! En réalité, l'échec est une bonne chose: cela nous indique où concentrer nos efforts. Le premier problème grave est que tddjs n'existe pas. Ajoutons l'objet de l'espace de noms dans src / observable.js
:
var tddjs = ;
Une nouvelle exécution des tests génère une nouvelle erreur:
E Total 1 tests (réussite: 0; échec: 0; erreurs: 1) (0.00 ms) Firefox 3.6.12 Linux: exécuter 1 tests (réussite: 0; échecs: 0; erreurs 1) (0.00 ms) ObservableAddObserverTest.test devrait Erreur de la fonction de stockage (0.00 ms): \ tddjs.Observable n'est pas un constructeur /test/observable_test.js:3 Les tests ont échoué..
Nous pouvons résoudre ce nouveau problème en ajoutant un constructeur vide Observable:
var tddjs = ; (function () function Observable () tddjs.Observable = Observable; ());
Une fois de plus, le test nous amène directement au problème suivant:
E Total 1 tests (réussite: 0; échec: 0; erreurs: 1) (0.00 ms) Firefox 3.6.12 Linux: exécuter 1 tests (réussite: 0; échecs: 0; erreurs 1) (0.00 ms) ObservableAddObserverTest.test devrait Erreur de fonction de stockage (0.00 ms): \ observable.addObserver n'est pas une fonction /test/observable_test.js:6 Échec des tests.
Ajoutons la méthode manquante.
function addObserver () Observable.prototype.addObserver = addObserver;
Avec la méthode en place, le test échoue maintenant à la place d'un tableau d'observateurs manquant..
E Total 1 tests (réussite: 0; échec: 0; erreurs: 1) (0.00 ms) Firefox 3.6.12 Linux: exécuter 1 tests (réussite: 0; échecs: 0; erreurs 1) (0.00 ms) ObservableAddObserverTest.test devrait erreur de la fonction de stockage (0,00 ms): \ observable.observers n’est pas défini /test/observable_test.js:8 Échec des tests.
Aussi étrange que cela puisse paraître, je vais maintenant définir le tableau des observateurs à l'intérieur du pubsub
méthode. Quand un test échoue, TDD nous demande de faire la chose la plus simple qui puisse fonctionner, peu importe à quel point elle se sent sale. Nous aurons la chance de revoir notre travail une fois le test réussi.
fonction addObserver (observateur) this.observers = [observateur]; Succès! Le test passe maintenant:. Total 1 tests (réussi: 1; échec: 0; erreurs: 0) (1,00 ms) Firefox 3.6.12 Linux: exécuter 1 tests (réussi: 1; échec: 0; erreurs 0) (1,00 ms)
Lors du développement de la solution actuelle, nous avons emprunté la voie la plus rapide possible pour réussir le test. Maintenant que la barre est verte, nous pouvons examiner la solution et effectuer toute refactorisation jugée nécessaire. La seule règle dans cette dernière étape est de garder la barre verte. Cela signifie que nous devrons également refactoriser par petites étapes, en nous assurant de ne rien casser accidentellement.
La mise en œuvre actuelle comporte deux problèmes que nous devrions traiter. Le test repose sur des hypothèses détaillées concernant la mise en œuvre d’Observable et la addObserver
la mise en œuvre est codée en dur à notre test.
Nous allons d'abord aborder le codage en dur. Pour exposer la solution codée en dur, nous allons augmenter le test pour lui faire ajouter deux observateurs au lieu d’un.
"test devrait stocker la fonction": function () var observable = new tddjs.Observable (); var observateurs = [fonction () , fonction () ]; observable.addObserver (observateurs [0]); observable.addObserver (observateurs [1]); assertEquals (observateurs, observables.observateurs);
Comme prévu, le test échoue maintenant. Le test suppose que les fonctions ajoutées en tant qu’observateurs doivent s’empiler comme tout élément ajouté à une pubsub
. Pour ce faire, nous allons déplacer l'instanciation du tableau dans le constructeur et simplement déléguer addObserver
au tableau
méthode push:
fonction Observable () this.observers = []; function addObserver (observateur) this.observers.push (observateur);
Avec cette implémentation en place, le test réussit à nouveau, prouvant que nous avons pris soin de la solution codée en dur. Cependant, la question de l'accès à une propriété publique et de la formulation d'hypothèses sauvages sur la mise en œuvre d'Observable est toujours d'actualité. Une observable pubsub
devrait être observable par un nombre quelconque d'objets, mais cela ne présente aucun intérêt pour les étrangers comment et où l'observable les stocke. Idéalement, nous aimerions pouvoir vérifier avec l'observable si un certain observateur est enregistré sans tâtonner à l'intérieur. Nous prenons note de l'odeur et passons à autre chose. Plus tard, nous reviendrons pour améliorer ce test.
Nous allons ajouter une autre méthode à Observable, hasObserver
, et l'utiliser pour supprimer une partie du fouillis que nous avons ajouté lors de la mise en œuvre addObserver
.
Une nouvelle méthode commence par un nouveau test et le prochain comportement souhaité pour le hasObserver
méthode.
TestCase ("ObservableHasObserverTest", "le test doit retourner la valeur true si observer": function () var observable = nouveau tddjs.Observable (); var observer = fonction () ; observable.addObserver (observateur); assertTrue (observable .hasObserver (observateur)););
Nous nous attendons à ce que ce test échoue face à un manquant. hasObserver
, ce qu'il fait.
Encore une fois, nous utilisons la solution la plus simple pouvant passer le test actuel:
fonction hasObserver (observateur) return true; Observable.prototype.hasObserver = hasObserver;
Même si nous savons que cela ne réglera pas nos problèmes à long terme, les tests resteront écologiques. Essayer de réviser et de refactoriser nous laisse les mains vides car il n’ya pas de points évidents où nous pouvons nous améliorer. Les tests sont nos exigences, et actuellement ils ne nécessitent que hasObserver
pour revenir vrai. Pour résoudre ce problème, nous allons introduire un autre test qui attend hasObserver
à retourne faux
pour un observateur inexistant, ce qui peut aider à forcer la vraie solution.
"test doit retourner false si aucun observateur": function () var observable = new tddjs.Observable (); assertFalse (observable.hasObserver (function () ));
Ce test échoue lamentablement, étant donné que hasObserver
toujours retourne vrai,
nous obligeant à produire la mise en œuvre réelle. Pour vérifier si un observateur est enregistré, il suffit de vérifier que le tableau this.observers contient l’objet initialement transmis à addObserver
:
fonction hasObserver (observateur) renvoie this.observers.indexOf (observateur)> = 0;
le Array.prototype.indexOf
méthode retourne un nombre inférieur à 0
si l'élément n'est pas présent dans le tableau
, afin de vérifier qu'il renvoie un nombre égal ou supérieur à 0
nous dira si l'observateur existe.
L'exécution du test dans plusieurs navigateurs donne des résultats quelque peu surprenants:
chris @ ordinateur portable: ~ / projets / observable $ jstestdriver - teste tout… E Total 4 tests (Réussite: 3; Fails: 0; Erreurs: 1) (11.00 ms) Firefox 3.6.12 Linux: Exécuter 2 tests (Réussie: 2 ; Fails: 0; Erreurs 0) (2.00 ms) Microsoft Internet Explorer 6.0 Windows: Exécutez 2 tests \ (Passé: 1; Fails: 0; Erreurs 1) (0,00 ms) ObservableHasObserverTest.test doit retourner à vrai quand a une erreur d'observateur \ ( 0,00 ms): l'objet ne prend pas en charge cette propriété ou cette méthode. Les tests ont échoué..
Les versions 6 et 7 d'Internet Explorer ont échoué au test avec leur message d'erreur le plus générique: "L'objet ne supporte pas cette propriété ou cette méthode ".
Cela peut indiquer un nombre quelconque de problèmes:
Heureusement, TDD-ing par petites étapes, nous savons que l’erreur doit concerner l’appel récemment ajouté à Indice de
sur nos observateurs tableau
. Il s'avère que IE 6 et 7 ne supportent pas la méthode JavaScript 1.6 Array.prototype.indexOf
(dont on ne peut pas vraiment le blâmer, il n’a été que récemment normalisé avec ECMAScript 5, décembre 2009). À ce stade, nous avons trois options:
Laquelle de ces approches est la mieux adaptée pour résoudre un problème donné dépendra de la situation - elles ont toutes leurs avantages et leurs inconvénients. Dans l’intérêt de garder Observable autonome, nous allons simplement mettre en œuvre hasObserver
en termes d'une boucle à la place de la Indice de
appel, travaillant efficacement autour du problème. Incidemment, cela semble aussi être la chose la plus simple qui puisse fonctionner à ce stade. Si nous rencontrions une situation similaire ultérieurement, il nous serait conseillé de reconsidérer notre décision. La mise à jour hasObserver
se présente comme suit:
fonction hasObserver (observateur) pour (var i = 0, l = this.observers.length; i < l; i++) if (this.observers[i] == observer) return true; return false;
Avec la barre verte, il est temps de passer en revue nos progrès. Nous avons maintenant trois tests, mais deux d'entre eux semblent étrangement similaires. Le premier test que nous avons écrit pour vérifier l'exactitude de addObserver
essentiellement des tests pour les mêmes choses que le test que nous avons écrit pour vérifier Refactoring
. Il existe deux différences essentielles entre les deux tests: le premier test a déjà été déclaré odorant, car il accède directement au tableau d'observateurs à l'intérieur de l'objet observable. Le premier test ajoute deux observateurs, assurant qu'ils sont tous les deux ajoutés. Nous pouvons maintenant joindre les tests dans un test qui vérifie que tous les observateurs ajoutés à l'observable sont réellement ajoutés:
"test devrait stocker des fonctions": function () var observable = new tddjs.Observable (); var observateurs = [fonction () , fonction () ]; observable.addObserver (observateurs [0]); observable.addObserver (observateurs [1]); assertTrue (observable.hasObserver (observateurs [0])); assertTrue (observable.hasObserver (observateurs [1]));
Ajouter des observateurs et vérifier leur existence est une bonne chose, mais sans la possibilité de les informer de changements intéressants, Observable n’est pas très utile. Il est temps d'implémenter la méthode notify.
La tâche la plus importante que notification effectue consiste à appeler tous les observateurs. Pour ce faire, nous avons besoin d'un moyen de vérifier qu'un observateur a bien été appelé après coup. Pour vérifier qu'une fonction a été appelée, nous pouvons définir une propriété sur la fonction quand elle est appelée. Pour vérifier le test, nous pouvons vérifier si la propriété est définie. Le test suivant utilise ce concept dans le premier test pour notify.
TestCase ("ObservableNotifyTest", "le test doit appeler tous les observateurs": function () var observable = new tddjs.Observable (); var observer1 = fonction () observer1.called = true;; var observer2 = fonction () observ2.called = true;; observable.addObserver (observateur1); observable.addObserver (observateur2); observable.notify (); assertTrue (observateur1.callé); assertTrue (observateur2.callé););
Pour réussir le test, nous devons boucler le tableau d'observateurs et appeler chaque fonction:
fonction notify () for (var i = 0, l = this.observers.length; i < l; i++) this.observers[i](); Observable.prototype.notify = notify;
Actuellement, les observateurs sont appelés, mais ils ne reçoivent aucune donnée. Ils savent que quelque chose s'est passé - mais pas nécessairement quoi. Nous ferons en sorte que notifie plusieurs arguments, en les transmettant simplement à chaque observateur:
"test devrait passer par les arguments": function () var observable = new tddjs.Observable (); var réel; observable.addObserver (function () actual = arguments;); observable.notify ("String", 1, 32); assertEquals (["String", 1, 32], actuel);
Le test compare les arguments reçus et transmis en affectant les arguments reçus à une variable locale au test. L'observateur que nous venons de créer est en fait un espion de test manuel très simple. L'exécution du test confirme son échec, ce qui n'est pas surprenant, car nous ne touchons actuellement pas les arguments contenus dans notify..
Pour réussir le test, nous pouvons utiliser appliquer lorsque vous appelez l'observateur:
fonction notify () for (var i = 0, l = this.observers.length; i < l; i++) this.observers[i].apply(this, arguments);
Avec ce correctif simple, les tests reviennent au vert. Notez que nous avons envoyé ceci comme premier argument à appliquer, ce qui signifie que les observateurs seront appelés avec l'observable comme ceci.
À ce stade, Observable est fonctionnel et des tests permettent de vérifier son comportement. Cependant, les tests vérifient uniquement que les éléments observables se comportent correctement en réponse aux entrées attendues. Que se passe-t-il si quelqu'un essaie d'enregistrer un objet en tant qu'observateur à la place d'une fonction? Que se passe-t-il si l'un des observateurs explose? Ce sont des questions auxquelles nous devons répondre à nos tests. Il est important d’assurer un comportement correct dans les situations attendues - c’est ce que nos objets feront la plupart du temps. Au moins pour pouvoir espérer. Cependant, un comportement correct, même lorsque le client se comporte mal, est tout aussi important pour garantir un système stable et prévisible.
L’implémentation actuelle accepte aveuglément tout type d’argument pour addObserver
. Bien que notre implémentation puisse utiliser n'importe quelle fonction en tant qu'observateur, elle ne peut gérer aucune valeur. Le test suivant s'attend à ce que l'observable lève une exception lors d'une tentative d'ajout d'un observateur non appelable..
"test devrait lancer pour un observateur invincable": function () var observable = new tddjs.Observable (); assertException (function () observable.addObserver ();, "TypeError");
En lançant déjà une exception lors de l'ajout des observateurs, nous n'avons pas besoin de nous soucier des données non valides ultérieurement lorsque nous en informons les observateurs. Si nous avions programmé par contrat, nous pourrions dire qu’une condition préalable à la addObserver
méthode est que l'entrée doit être appelable. le postcondition
est que l'observateur est ajouté à l'observable et qu'il est garanti d'être appelé une fois que les appels observables ont.
Le test échouant, nous nous concentrons donc sur l’obtention de la barre verte au plus vite. Malheureusement, il n’ya aucun moyen de simuler l’implémentation en lançant une exception sur tout appel à addObserver
échouera à tous les autres tests. Heureusement, la mise en œuvre est assez triviale:
fonction addObserver (observateur) if (typeof observer! = "fonction") lance un nouveau TypeError ("observateur n'est pas une fonction"); this.observers.push (observateur);
addObserver
vérifie maintenant que l'observateur est en fait une fonction avant de l'ajouter à la liste. Effectuer les tests procure cette douce sensation de réussite: tout vert.
L’observable garantit désormais que tout observateur ajouté par le biais de addObserver
est appelable. Néanmoins, notify peut toujours échouer horriblement si un observateur lève une exception. Le prochain test s'attend à ce que tous les observateurs soient appelés, même si l'un d'eux lève une exception..
"test devrait notifier tout même quand certains échouent": function () var observable = new tddjs.Observable (); var observer1 = fonction () lance une nouvelle erreur ("Oups"); ; var observer2 = fonction () observer2.called = true; ; observable.addObserver (observateur1); observable.addObserver (observateur2); observable.notify (); assertTrue (observateur2.callé);
L'exécution du test révèle que l'implémentation actuelle explose en même temps que le premier observateur, ce qui empêche le second observateur d'être appelé. Effectivement, notify brise sa garantie de toujours appeler tous les observateurs une fois qu’ils ont été ajoutés avec succès. Pour rectifier la situation, la méthode doit être préparée au pire:
fonction notify () for (var i = 0, l = this.observers.length; i < l; i++) try this.observers[i].apply(this, arguments); catch (e)
L'exception est éliminée silencieusement. Il incombe à l’observateur de s’assurer que toutes les erreurs sont traitées correctement. L’observable est tout simplement pour repousser les observateurs qui se comportent mal..
Nous avons amélioré la robustesse du module Observable en lui donnant une gestion correcte des erreurs. Le module est maintenant en mesure de donner des garanties de fonctionnement tant qu’il obtient une bonne entrée et qu’il est capable de récupérer si un observateur ne répondait pas à ses exigences. Cependant, le dernier test que nous avons ajouté émet une hypothèse sur les caractéristiques non documentées de l'observable: il suppose que les observateurs sont appelés dans l'ordre dans lequel ils ont été ajoutés. Actuellement, cette solution fonctionne car nous avons utilisé un tableau pour implémenter la liste des observateurs. Si nous décidons de changer cela, cependant, nos tests pourraient échouer. Nous devons donc décider: est-ce que nous refactorisons le test pour ne pas supposer une commande d’appel, ou ajoutons-nous simplement un test qui attend une commande d’appel - documentant ainsi la commande d’appel en tant que fonctionnalité? L'ordre des appels semble être une fonction utile. Notre prochain test s'assurera que Observable garde ce comportement..
"test doit appeler les observateurs dans l'ordre dans lequel ils ont été ajoutés": function () var observable = new tddjs.Observable (); var appelle = []; var observer1 = fonction () calls.push (observateur1); ; var observer2 = fonction () calls.push (observer2); ; observable.addObserver (observateur1); observable.addObserver (observateur2); observable.notify (); assertEquals (observateur1, appels [0]); assertEquals (observateur2, appels [1]);
Comme la mise en œuvre utilise déjà un tableau pour les observateurs, ce test réussit immédiatement..
Dans les langages statiques à héritage classique, les objets arbitraires sont rendus observables par sous-classement la classe observable. La motivation pour l'héritage classique dans ces cas vient d'un désir de définir les mécanismes du motif en un seul endroit et de réutiliser la logique sur une grande quantité d'objets non liés. En JavaScript, nous avons plusieurs options pour la réutilisation du code entre les objets. Il n'est donc pas nécessaire de se limiter à une émulation du modèle d'héritage classique..
Dans l'intérêt de vous libérer de l'émulation classique fournie par les constructeurs, considérons les exemples suivants qui supposent que tddjs.observable est un objet plutôt qu'un constructeur:
Noter la tddjs.extend
La méthode est introduite ailleurs dans le livre et copie simplement les propriétés d'un objet à un autre.
// Création d'un seul objet observable var observable = Object.create (tddjs.util.observable); // Extension d'un objet unique tddjs.extend (journal, tddjs.util.observable); // Un constructeur qui crée des objets observables function Newspaper () / *… * / Newspaper.prototype = Object.create (tddjs.util.observable); // Extension d'un prototype existant tddjs.extend (Newspaper.prototype, tddjs.util.observable);
La simple mise en œuvre de l'observable en tant qu'objet unique offre une grande flexibilité. Pour y arriver, nous devons refactoriser la solution existante pour supprimer le constructeur..
Pour se débarrasser du constructeur, nous devons d’abord procéder à un refactorage Observable de sorte que le constructeur ne fasse aucun travail. Heureusement, le constructeur initialise uniquement le tableau d'observateurs, ce qui ne devrait pas être trop difficile à supprimer. Toutes les méthodes de Observable.prototype accèdent au tableau. Nous devons donc nous assurer qu'elles peuvent toutes gérer le cas où il n'a pas été initialisé. Pour tester cela, nous devons simplement écrire un test par méthode qui appelle la méthode en question avant de faire quoi que ce soit d'autre..
Comme nous avons déjà des tests qui appellent addObserver
et hasObserver
avant de faire quoi que ce soit, nous allons nous concentrer sur la méthode notify. Cette méthode est testée seulement après addObserver
a été appelé. Nos prochains tests s’attendent à ce qu’il soit possible d’appeler cette méthode avant d’ajouter des observateurs..
"test ne devrait pas échouer si aucun observateur": function () var observable = new tddjs.Observable (); assertNoException (function () observable.notify (););
Avec ce test en place, nous pouvons vider le constructeur:
fonction Observable ()
L'exécution des tests montre que tous sauf un échouent, tous avec le même message: "this.observers n'est pas défini". Nous traiterons une méthode à la fois. La première place est addObserver
méthode:
fonction addObserver (observateur)
si (! this.observers)
this.observers = [];
/ *… * /
Une nouvelle exécution des tests révèle que la mise à jour addObserver
méthode corrige tous les tests sauf les deux qui ne commencent pas par l’appeler. Ensuite, nous nous assurons de renvoyer faux directement de hasObserver
si le tableau n'existe pas.
fonction hasObserver (observateur) if (! this.observers) return false; / *… * /
Nous pouvons appliquer le même correctif pour notifier:
fonction notify (observateur) if (! this.observers) return; / *… * /
Maintenant que le constructeur
ne fait rien, il peut être retiré en toute sécurité. Nous ajouterons ensuite toutes les méthodes directement au tddjs.observable
objet
, qui peut ensuite être utilisé avec par ex. Object.create ou tddjs.extend
créer des objets observables. Notez que le nom n'est plus en majuscule car il ne s'agit plus d'un constructeur. L'implémentation mise à jour suit:
(function () function addObserver (observateur) / *… * / function hasObserver (observateur) / *… * / function notify () / *… * / tddjs.observable = addObserver: addObserver, hasObserver : hasObserver, notify: notify; ());
Supprimer le constructeur provoque sûrement la rupture de tous les tests jusqu’à présent. Les réparer est facile, cependant. Tout ce que nous avons à faire est de remplacer la nouvelle déclaration par un appel à Object.create
. Cependant, la plupart des navigateurs ne supportent pas Object.create
pourtant, alors nous pouvons le casser. Comme la méthode n’est pas possible de s’imuler parfaitement, nous fournirons notre propre version sur le tddjs
objet
:
(function () fonction F () tddjs.create = fonction (objet) F.prototype = objet; renvoie le nouveau F ();; / * La mise en œuvre observable va ici… * / ());
Avec la cale en place, nous pouvons mettre à jour les tests de manière à pouvoir fonctionner même avec les anciens navigateurs. La suite de tests finale suit:
TestCase ("ObservableAddObserverTest", setUp: function () this.observable = tddjs.create (tddjs.observable);, "le test doit stocker des fonctions": function () var observateurs = [fonction () , fonction () ]; this.observable.addObserver (observateurs [0]); this.observable.addObserver (observateurs [1]); assertTrue (this.observable.hasObserver (observateurs [0])); assertTrue (this.observable .hasObserver (observers [1]));