Comment testez-vous une fonction privée en angular 2?
class FooBar { private _status: number; constructor( private foo : Bar ) { this.initFooBar(); } private initFooBar(){ this.foo.bar( "data" ); this._status = this.fooo.foo(); } public get status(){ return this._status; } }
La solution que j’ai trouvée
Placez le code de test lui-même dans la fermeture ou Ajouter le code dans la fermeture qui stocke les références aux variables locales sur les objects existants dans la scope externe.
Plus tard, supprimez le code de test à l’aide d’un outil. http://philipwalton.com/articles/how-to-unit-test-private-functions-in-javascript/
S’il vous plaît me suggérer une meilleure façon de résoudre ce problème si vous en avez fait?
PS
La plupart des réponses à ce type de questions, comme celle-ci, ne permettent pas de résoudre le problème, c’est pourquoi je pose cette question.
La plupart des développeurs disent que vous ne testez pas les fonctions privées mais je ne dis pas qu’ils ont tort ou raison, mais il y a des besoins pour que mon cas teste en privé.
Je suis avec vous, même si l’objective est de “tester uniquement l’API publique”, il y a des moments où cela ne semble pas si simple et vous sentez que vous choisissez entre compromettre l’API ou les tests unitaires. Vous le savez déjà, puisque c’est exactement ce que vous demandez, donc je ne vais pas y entrer. 🙂
Dans TypeScript, j’ai découvert quelques moyens d’accéder aux membres privés pour tester vos unités. Considérez cette classe:
class MyThing { private _name:ssortingng; private _count:number; constructor() { this.init("Test", 123); } private init(name:ssortingng, count:number){ this._name = name; this._count = count; } public get name(){ return this._name; } public get count(){ return this._count; } }
Même si TS restreint l’access aux membres de classe à l’aide de private
, protected
, public
, le JS compilé n’a aucun membre privé, car ce n’est pas une chose dans JS. C’est uniquement utilisé pour le compilateur TS. A cette fin:
Vous pouvez affirmer à any
et échapper au compilateur de vous avertir des ressortingctions d’access:
(thing as any)._name = "Unit Test"; (thing as any)._count = 123; (thing as any).init("Unit Test", 123);
Le problème avec cette approche est que le compilateur n’a tout simplement aucune idée de ce que vous faites, de sorte que vous n’obtenez pas les erreurs de type souhaitées:
(thing as any)._name = 123; // wrong, but no error (thing as any)._count = "Unit Test"; // wrong, but no error (thing as any).init(0, "123"); // wrong, but no error
Vous pouvez utiliser l’access au tableau ( []
) pour accéder aux membres privés:
thing["_name"] = "Unit Test"; thing["_count"] = 123; thing["init"]("Unit Test", 123);
Bien que cela semble génial, TSC validera les types comme si vous y aviez accédé directement:
thing["_name"] = 123; // type error thing["_count"] = "Unit Test"; // type error thing["init"](0, "123"); // argument error
Pour être honnête, je ne sais pas pourquoi cela fonctionne. Il semble que les parenthèses ne mettent pas en application les ressortingctions d’access, mais l’inférence de type vous offre une sécurité complète. C’est exactement ce que je pense que vous voulez pour vos tests unitaires.
Voici un exemple de travail dans TypeScript Playground .
Comme la plupart des développeurs ne recommandent pas de tester la fonction privée , pourquoi ne pas la tester?
Par exemple.
YourClass.ts
export class FooBar { private _status: number; constructor( private foo : Bar ) { this.initFooBar({}); } private initFooBar(data){ this.foo.bar( data ); this._status = this.foo.foo(); } }
TestYourClass.spec.ts
describe("Testing foo bar for status being set", function() { ... //Variable with type any let fooBar; fooBar = new FooBar(); ... //Method 1 //Now this will be visible fooBar.initFooBar(); //Method 2 //This doesn't require variable with any type fooBar['initFooBar'](); ... }
Merci à @Aaron, @Thierry Templier.
Ne pas écrire de tests pour les méthodes privées. Cela va à l’encontre des tests du point d’unité.
Exemple
class SomeClass { public addNumber(a: number, b: number) { return a + b; } }
Le test de cette méthode ne doit pas nécessairement changer si, par la suite, l’implémentation change, mais le behaviour
de l’API publique rest le même.
class SomeClass { public addNumber(a: number, b: number) { return this.add(a, b); } private add(a: number, b: number) { return a + b; } }
Ne pas rendre les méthodes et les propriétés publiques uniquement pour les tester. Cela signifie généralement que soit:
Désolé pour le necro sur ce post, mais je me sens obligé de peser sur quelques points qui ne semblent pas avoir été abordés.
En premier lieu, lorsque nous avons besoin d’accéder à des membres privés dans une classe lors des tests unitaires, c’est généralement un gros drapeau rouge que nous avons manqué dans notre approche stratégique ou tactique et qui ont violé par inadvertance le principe de responsabilité unique en poussant comportement où il n’appartient pas. Sentir le besoin d’accéder à des méthodes qui ne sont rien de plus qu’un sous-programme isolé d’une procédure de construction en est l’une des occurrences les plus courantes. Cependant, c’est un peu comme si votre patron s’attendait à ce que vous vous présentiez pour le travail prêt à l’emploi et que vous ayez également besoin d’un peu de perversité pour savoir quelle routine du matin vous avez suivie pour vous mettre dans cet état …
L’autre exemple le plus courant de ce phénomène est lorsque vous essayez de tester la proverbiale “classe de dieu”. C’est un type de problème particulier en soi, mais souffre du même problème fondamental, à savoir le besoin de connaître les détails intimes d’une procédure – mais cela ne fait pas partie du sujet.
Dans cet exemple spécifique, nous avons assigné la responsabilité d’initialiser complètement l’object Bar au constructeur de la classe FooBar. Dans la programmation orientée object, l’un des principes fondamentaux est que le constructeur est «sacré» et doit être protégé contre les données non valides qui invalideraient son propre état interne et le laisserait tomber en panne ailleurs (dans ce qui pourrait être très profond). pipeline.)
Nous n’avons pas réussi à le faire ici en autorisant l’object FooBar à accepter une Barre qui n’était pas prête au moment de la construction du FooBar, et à compenser en “piratant” l’object FooBar pour prendre les choses en main. mains.
Ceci est le résultat d’une incapacité à adhérer à un autre principe de programmation orientée object (dans le cas de Bar), à savoir que l’état d’un object doit être entièrement initialisé et prêt à traiter tous les appels entrants à ses membres publics immédiatement après la création. Maintenant, cela ne signifie pas immédiatement après l’appel du constructeur dans toutes les instances. Lorsque vous avez un object comportant de nombreux scénarios de construction complexes, il est préférable d’exposer les parameters à ses membres facultatifs à un object implémenté conformément à un modèle de création (Factory, Builder, etc …). Dans ce dernier cas, vous lanceriez l’initialisation de l’object cible dans un autre graphe d’object dont le seul but est de diriger le trafic pour vous amener à une instance valide de ce que vous demandez – et le produit ne devrait pas être considéré comme “prêt” jusqu’à ce que cet object de création l’a servi.
Dans votre exemple, la propriété “status” de la barre ne semble pas être dans un état valide dans lequel un FooBar peut l’accepter – le FooBar lui fait donc quelque chose pour corriger ce problème.
Le deuxième problème que je constate, c’est que vous essayez de tester votre code plutôt que de pratiquer le développement piloté par les tests. Ceci est certainement ma propre opinion à ce stade; mais ce type de test est vraiment un anti-modèle. Vous finissez par tomber dans le piège consistant à vous rendre compte que vous rencontrez des problèmes de conception de base qui empêchent de tester votre code après coup, plutôt que d’écrire les tests dont vous avez besoin et ensuite de programmer les tests. Quoi qu’il en soit, vous devriez vous retrouver avec le même nombre de tests et de lignes de code si vous aviez vraiment réalisé une implémentation SOLID. Alors, pourquoi essayer de faire de l’ingénierie inverse dans le code testable lorsque vous ne pouvez résoudre le problème qu’au début de vos efforts de développement?
Si vous l’aviez fait, alors vous vous seriez rendu compte beaucoup plus tôt que vous deviez écrire du code plutôt difficile pour tester votre conception et avoir eu l’occasion de réaligner votre approche en déplaçant le comportement vers des implémentations sont facilement testables.
Le sharepoint “ne pas tester les méthodes privées” est vraiment de tester la classe comme quelqu’un qui l’utilise .
Si vous avez une API publique avec 5 méthodes, tout consommateur de votre classe peut les utiliser et vous devriez donc les tester. Un consommateur ne doit pas accéder aux méthodes / propriétés privées de votre classe, ce qui signifie que vous pouvez modifier les membres privés lorsque la fonctionnalité exposée publique rest la même.
Si vous utilisez protected
fonctionnalités extensibles internes, utilisez protected
au lieu de private
.
Notez que protected
est toujours une API publique (!) , Juste utilisée différemment.
class OverlyComplicatedCalculator { public add(...numbers: number[]): number { return this.calculate((a, b) => a + b, numbers); } // can't be used or tested via ".calculate()", but it is still part of your public API! protected calculate(operation, operands) { let result = operands[0]; for (let i = 1; i < operands.length; operands++) { result = operation(result, operands[i]); } return result; } }
Unités testées des propriétés protégées de la même manière qu'un consommateur les utilisera, par sous-classement:
it('should be extensible via calculate()', () => { class TestCalculator extends OverlyComplicatedCalculator { public testWithArrays(array: any[]): any[] { const concat = (a, b) => [].concat(a, b); // tests the protected method return this.calculate(concat, array); } } let testCalc = new TestCalculator(); let result = testCalc.testWithArrays([1, 'two', 3]); expect(result).toEqual([1, 'two', 3]); });
Je suis d’accord avec @toskv: Je ne recommanderais pas de faire ça 🙂
Mais si vous voulez vraiment tester votre méthode privée, sachez que le code correspondant au TypeScript correspond à une méthode du prototype de la fonction constructeur. Cela signifie qu’il peut être utilisé à l’exécution (alors que vous aurez probablement des erreurs de compilation).
Par exemple:
export class FooBar { private _status: number; constructor( private foo : Bar ) { this.initFooBar({}); } private initFooBar(data){ this.foo.bar( data ); this._status = this.foo.foo(); } }
seront transposés dans:
(function(System) {(function(__moduleName){System.register([], function(exports_1, context_1) { "use ssortingct"; var __moduleName = context_1 && context_1.id; var FooBar; return { setters:[], execute: function() { FooBar = (function () { function FooBar(foo) { this.foo = foo; this.initFooBar({}); } FooBar.prototype.initFooBar = function (data) { this.foo.bar(data); this._status = this.foo.foo(); }; return FooBar; }()); exports_1("FooBar", FooBar); } } })(System);
Voir ce plunkr: https://plnkr.co/edit/calJCF?p=preview .
Comme beaucoup l’ont déjà dit, vous devriez tester vos méthodes privées, mais vous ne devriez pas pirater votre code ou votre transstackr pour le faire fonctionner. De nos jours, TypeScript va nier la plupart des hacks que les gens ont fournis jusqu’à présent.
TLDR ; Si une méthode doit être testée, vous devez découpler le code en une classe que vous pouvez exposer à la méthode pour la tester.
La raison pour laquelle la méthode est privée est que la fonctionnalité n’appartient pas nécessairement à cette classe et que, par conséquent, si la fonctionnalité n’appartient pas à cette classe, elle doit être découplée dans sa propre classe.
Je suis tombé sur cet article qui explique très bien comment vous devriez essayer les méthodes privées. Il couvre même certaines des méthodes ici et comment elles sont de mauvaises implémentations.
https://pasortingckdesjardins.com/blog/how-to-unit-test-private-method-in-typescript-part-2
Remarque : Ce code est supprimé du blog lié ci-dessus (je duplique en cas de modification du contenu du lien)
Avant
class User{ public getUserInformationToDisplay(){ //... this.getUserAddress(); //... } private getUserAddress(){ //... this.formatStreet(); //... } private formatStreet(){ //... } }
Après
class User{ private address:Address; public getUserInformationToDisplay(){ //... address.getUserAddress(); //... } } class Address{ private format: StreetFormatter; public format(){ //... format.ToSsortingng(); //... } } class StreetFormatter{ public toSsortingng(){ // ... } }
La réponse d’ Aaron est la meilleure et travaille pour moi 🙂 Je voterais mais malheureusement je ne peux pas (manque de réputation).
Je dois dire que tester des méthodes privées est la seule façon de les utiliser et d’avoir un code propre de l’autre côté.
Par exemple:
class Something { save(){ const data = this.getAllUserData() if (this.validate(data)) this.sendRequest(data) } private getAllUserData () {...} private validate(data) {...} private sendRequest(data) {...} }
Il est tout à fait logique de ne pas tester toutes ces méthodes en même temps, car nous aurions besoin de simuler ces méthodes privées, dont nous ne pouvons pas nous défaire car nous ne pouvons pas y accéder. Cela signifie que nous avons besoin de beaucoup de configuration pour un test unitaire afin de le tester dans son ensemble.
Ceci dit, le meilleur moyen de tester la méthode ci-dessus avec toutes les dépendances est un test de bout en bout, car ici un test d’intégration est nécessaire, mais le test E2E ne vous aidera pas si vous pratiquez le TDD (Test Driven Development). toute méthode sera.