Comment savoir ce qu'il faut tester lors de l'écriture de tests unitaires? [fermé]


127

En utilisant C #, j'ai besoin d'une classe appelée Userqui a un nom d'utilisateur, un mot de passe, un drapeau actif, un prénom, un nom, un nom complet, etc.

Il devrait y avoir des méthodes pour authentifier et enregistrer un utilisateur. Dois-je juste écrire un test pour les méthodes? Et ai-je même besoin de m'inquiéter de tester les propriétés car ce sont les getter et les setters de .Net?


Cet article vous aidera à réduire la question plus large: Earnestengineer.blogspot.com/2018/03 / ... Vous pouvez suivre ces directives pour concentrer votre question
Lindsay Morsillo

Gardez à l'esprit que les mots de passe ne doivent pas être stockés en texte brut.
Mr Anderson

Réponses:


131

De nombreuses réponses intéressantes à cela se trouvent également sur ma question: " Début du TDD - Défis? Solutions? Recommandations? "

Puis-je également recommander de jeter un œil à mon article de blog (qui a été en partie inspiré par ma question), j'ai de bons retours à ce sujet. À savoir:

Je ne sais pas où commencer?

  • Recommence. Ne pensez à écrire des tests que lorsque vous écrivez un nouveau code. Cela peut être une retouche de l'ancien code ou une fonctionnalité complètement nouvelle.
  • Commencez simplement. Ne vous précipitez pas et n'essayez pas de vous familiariser avec un cadre de test en plus d'être TDD-esque. Debug.Assert fonctionne correctement. Utilisez-le comme point de départ. Cela ne gâche pas votre projet et ne crée pas de dépendances.
  • Commencez positivement. Vous essayez d'améliorer votre métier, vous vous sentez bien. J'ai vu beaucoup de développeurs qui sont heureux de stagner et de ne pas essayer de nouvelles choses pour s'améliorer. Vous faites la bonne chose, souvenez-vous de cela et cela vous aidera à ne pas abandonner.
  • Commencez prêt pour un défi. Il est assez difficile de se lancer dans les tests. Attendez-vous à un défi, mais rappelez-vous - les défis peuvent être surmontés.

Testez uniquement ce que vous attendez

J'ai eu de vrais problèmes quand j'ai commencé parce que j'étais constamment assis là à essayer de comprendre tous les problèmes possibles qui pourraient survenir, puis à essayer de le tester et de le résoudre. C'est un moyen rapide de mal de tête. Les tests doivent être un véritable processus YAGNI. Si vous savez qu'il y a un problème, faites un test pour celui-ci. Sinon, ne vous inquiétez pas.

Tester seulement une chose

Chaque cas de test ne doit tester qu'une seule chose. Si jamais vous vous surprenez à mettre «et» dans le nom du scénario de test, vous faites quelque chose de mal.

J'espère que cela signifie que nous pouvons passer des "getters et setters" :)


2
"Si vous savez qu'il y a un problème, alors faites un test pour lui. Sinon, ne vous inquiétez pas." Je ne suis pas d'accord avec la façon dont cela est formulé. J'avais l'impression que les tests unitaires devraient couvrir tous les chemins d'exécution possibles.
Corin Blaikie

3
Alors que certains peuvent préconiser de telles choses, je ne le fais pas personnellement. Un bon 90% de mes maux de tête venaient d'essayer simplement de «tout». Je dis teste ce que vous attendez (pour que vous sachiez que vous récupérez les bonnes valeurs) mais n'essayez pas de tout comprendre. YAGNI.
Rob Cooper

4
Moi aussi, je préconise l'approche «testez vos bogues». Si nous avions tous un temps et une patience infinis, nous testerions tous les chemins d'exécution possibles. Mais nous ne le faisons pas, vous devez donc consacrer vos efforts là où cela aura le plus grand effet.
Schwern

63

Testez votre code, pas la langue.

Un test unitaire comme:

Integer i = new Integer(7);
assert (i.instanceOf(integer));

n'est utile que si vous écrivez un compilateur et qu'il y a une chance non nulle que votre instanceofméthode ne fonctionne pas.

Ne testez pas des éléments dont vous pouvez vous fier au langage à appliquer. Dans votre cas, je me concentrerais sur vos méthodes d'authentification et d'enregistrement - et j'écrirais des tests qui m'assuraient qu'ils pouvaient gérer les valeurs nulles dans tout ou partie de ces champs avec élégance.


1
Bon point sur "Ne pas tester le framework" - Quelque chose que j'ai aussi quand je suis nouveau dans ce domaine. + 1'ed :)
Rob Cooper

38

Cela m'a mis dans les tests unitaires et cela m'a rendu très heureux

Nous venons de commencer à faire des tests unitaires. Pendant longtemps, je savais que ce serait bien de commencer à le faire mais je ne savais pas par où commencer et surtout quoi tester.

Ensuite, nous avons dû réécrire un élément de code important dans notre programme de comptabilité. Cette partie était très complexe car elle impliquait de nombreux scénarios différents. La partie dont je parle est une méthode pour payer les factures de vente et / ou d'achat déjà saisies dans le système comptable.

Je ne savais tout simplement pas comment commencer à le coder, car il y avait tellement d'options de paiement différentes. Une facture peut être de 100 $, mais le client n'a transféré que 99 $. Vous avez peut-être envoyé des factures de vente à un client, mais vous avez également acheté auprès de ce client. Alors vous l'avez vendu pour 300 $ mais vous l'avez acheté pour 100 $. Vous pouvez vous attendre à ce que votre client vous paie 200 $ pour régler le solde. Et si vous vendiez 500 $ mais que le client ne vous paie que 250 $?

J'avais donc un problème très complexe à résoudre avec de nombreuses possibilités qu'un scénario fonctionnerait parfaitement mais serait erroné sur un autre type de combinaison invocie / paiement.

C'est là que les tests unitaires sont venus à la rescousse.

J'ai commencé à écrire (à l'intérieur du code de test) une méthode pour créer une liste de factures, à la fois pour les ventes et les achats. Ensuite, j'ai écrit une deuxième méthode pour créer le paiement réel. Normalement, un utilisateur saisit ces informations via une interface utilisateur.

Ensuite, j'ai créé la première TestMethod, testant un paiement très simple d'une seule facture sans aucune remise de paiement. Toutes les actions du système se produiraient lorsqu'un paiement bancaire serait enregistré dans la base de données. Comme vous pouvez le voir, j'ai créé une facture, créé un paiement (une transaction bancaire) et enregistré la transaction sur le disque. Dans mes affirmations, je mets quels devraient être les bons numéros se retrouvant dans la transaction bancaire et dans la facture liée. Je vérifie le nombre de paiements, les montants des paiements, le montant de la remise et le solde de la facture après la transaction.

Une fois le test exécuté, j'irais dans la base de données et vérifierais si ce à quoi je m'attendais était là.

Après avoir écrit le test, j'ai commencé à coder le mode de paiement (qui fait partie de la classe BankHeader). Dans le codage, je ne me suis préoccupé que du code pour passer le premier test. Je n'ai pas encore pensé aux autres scénarios, plus complexes.

J'ai exécuté le premier test, corrigé un petit bogue jusqu'à ce que mon test réussisse.

Puis j'ai commencé à écrire le deuxième test, cette fois en travaillant avec une remise de paiement. Après avoir effectué le test, j'ai modifié le mode de paiement pour prendre en charge les remises.

En testant l'exactitude avec une remise de paiement, j'ai également testé le paiement simple. Les deux tests devraient bien sûr réussir.

Ensuite, je suis descendu vers les scénarios les plus complexes.

1) Pensez à un nouveau scénario

2) Ecrire un test pour ce scénario

3) Exécutez ce test unique pour voir s'il réussirait

4) Si ce n'était pas le cas, je déboguerais et modifierais le code jusqu'à ce qu'il passe.

5) Tout en modifiant le code, j'ai continué à exécuter tous les tests

C'est ainsi que j'ai réussi à créer mon moyen de paiement très complexe. Sans test unitaire, je ne savais pas comment commencer à coder, le problème semblait écrasant. Avec les tests, je pourrais commencer avec une méthode simple et l'étendre étape par étape avec l'assurance que les scénarios les plus simples fonctionneraient toujours.

Je suis sûr que l'utilisation des tests unitaires m'a fait gagner quelques jours (ou semaines) de codage et garantit plus ou moins l'exactitude de ma méthode.

Si je pense plus tard à un nouveau scénario, je peux simplement l'ajouter aux tests pour voir s'il fonctionne ou non. Sinon, je peux modifier le code mais toujours être sûr que les autres scénarios fonctionnent toujours correctement. Cela permettra d'économiser des jours et des jours dans la phase de maintenance et de correction des bogues.

Oui, même le code testé peut toujours avoir des bogues si un utilisateur fait des choses auxquelles vous n'avez pas pensé ou l'a empêché de faire

Voici quelques-uns des tests que j'ai créés pour tester mon mode de paiement.

public class TestPayments
{
    InvoiceDiaryHeader invoiceHeader = null;
    InvoiceDiaryDetail invoiceDetail = null;
    BankCashDiaryHeader bankHeader = null;
    BankCashDiaryDetail bankDetail = null;



    public InvoiceDiaryHeader CreateSales(string amountIncVat, bool sales, int invoiceNumber, string date)
    {
        ......
        ......
    }

    public BankCashDiaryHeader CreateMultiplePayments(IList<InvoiceDiaryHeader> invoices, int headerNumber, decimal amount, decimal discount)
    {
       ......
       ......
       ......
    }


    [TestMethod]
    public void TestSingleSalesPaymentNoDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 1, "01-09-2008"));
        bankHeader = CreateMultiplePayments(list, 1, 119.00M, 0);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(119M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(0M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
    }

    [TestMethod]
    public void TestSingleSalesPaymentDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 2, "01-09-2008"));
        bankHeader = CreateMultiplePayments(list, 2, 118.00M, 1M);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(118M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(1M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
    }

    [TestMethod]
    [ExpectedException(typeof(ApplicationException))]
    public void TestDuplicateInvoiceNumber()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("100", true, 2, "01-09-2008"));
        list.Add(CreateSales("200", true, 2, "01-09-2008"));

        bankHeader = CreateMultiplePayments(list, 3, 300, 0);
        bankHeader.Save();
        Assert.Fail("expected an ApplicationException");
    }

    [TestMethod]
    public void TestMultipleSalesPaymentWithPaymentDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 11, "01-09-2008"));
        list.Add(CreateSales("400", true, 12, "02-09-2008"));
        list.Add(CreateSales("600", true, 13, "03-09-2008"));
        list.Add(CreateSales("25,40", true, 14, "04-09-2008"));

        bankHeader = CreateMultiplePayments(list, 5, 1144.00M, 0.40M);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(4, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(118.60M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(400, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
        Assert.AreEqual(600, bankHeader.BankCashDetails[0].Payments[2].PaymentAmount);
        Assert.AreEqual(25.40M, bankHeader.BankCashDetails[0].Payments[3].PaymentAmount);

        Assert.AreEqual(0.40M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].PaymentDiscount);

        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].InvoiceHeader.Balance);
    }

    [TestMethod]
    public void TestSettlement()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("300", true, 43, "01-09-2008")); //Sales
        list.Add(CreateSales("100", false, 6453, "02-09-2008")); //Purchase

        bankHeader = CreateMultiplePayments(list, 22, 200, 0);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(2, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(300, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(-100, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
    }

1
Vous avez trouvé un bogue dans votre test unitaire! Vous répétez cette ligne au lieu d'inclure un 2 dans l'un d'eux:Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].InvoiceHeader.Balance);
Ryan Peschel

2
Vous dites: "Une fois le test exécuté, j'irais dans la base de données et vérifierais si ce à quoi je m'attendais était là." Ceci est un bon exemple d'un test d'intégration entre les composants de votre système - pas un test unitaire isolé d'un seul morceau de code.
JTech

2
Vous avez également enfreint la règle de plus d'une affirmation par test.
Steve le

13

S'ils sont vraiment triviaux, ne vous inquiétez pas pour les tests. Par exemple, s'ils sont mis en œuvre comme ceci;

public class User
{
    public string Username { get; set; }
    public string Password { get; set; }
}

Si, d'un autre côté, vous faites quelque chose d'intelligent, (comme crypter et décrypter le mot de passe dans le getter / setter), alors testez-le.


10

La règle est que vous devez tester chaque élément de logique que vous écrivez. Si vous avez implémenté des fonctionnalités spécifiques dans les getters et les setters, je pense qu'elles valent la peine d'être testées. S'ils n'attribuent des valeurs qu'à certains champs privés, ne vous inquiétez pas.


6

Cette question semble être une question de savoir où peut-on tracer la ligne entre les méthodes testées et celles qui ne le sont pas.

Les setters et les getters pour l'attribution de valeur ont été créés en gardant à l'esprit la cohérence et la croissance future, et prévoyant qu'un certain temps plus tard, le setter / getter pourrait évoluer vers des opérations plus complexes. Il serait logique de mettre en place des tests unitaires de ces méthodes, également dans un souci de cohérence et de croissance future.

La fiabilité du code, en particulier lors de modifications pour ajouter des fonctionnalités supplémentaires, est l'objectif principal. Je ne suis pas au courant que quiconque ait été licencié pour avoir inclus des setters / getters dans la méthodologie de test, mais je suis certain qu'il existe des personnes qui auraient souhaité avoir testé des méthodes dont elles étaient au courant ou dont elles pouvaient se souvenir étaient de simples wrappers set / get, mais ce n'était pas plus l'affaire.

Peut-être qu'un autre membre de l'équipe a élargi les méthodes set / get pour inclure la logique qui doit maintenant être testée mais qui n'a pas ensuite créé les tests. Mais maintenant, votre code appelle ces méthodes et vous ne savez pas qu'elles ont changé et nécessitent des tests approfondis, et les tests que vous effectuez dans le développement et le contrôle qualité ne déclenchent pas le défaut, mais les données commerciales réelles le premier jour de la publication le font. déclenchez-le.

Les deux coéquipiers vont maintenant débattre pour savoir qui a laissé tomber le ballon et n'a pas réussi à faire des tests unitaires lorsque l'ensemble / est transformé pour inclure une logique qui peut échouer mais qui n'est pas couverte par un test unitaire. Le coéquipier qui a initialement écrit l'ensemble / obtient aura plus de facilité à sortir de ce nettoyage si les tests ont été implémentés dès le premier jour sur l'ensemble / obtient simple.

Mon opinion est que quelques minutes de temps «perdu» à couvrir TOUTES les méthodes avec des tests unitaires, même insignifiants, pourraient sauver des jours de maux de tête sur la route et une perte d'argent / réputation de l'entreprise et la perte du travail de quelqu'un.

Et le fait que vous ayez enveloppé des méthodes triviales avec des tests unitaires peut être vu par ce coéquipier junior quand ils changent les méthodes triviales en méthodes non triviales et les invitent à mettre à jour le test, et maintenant personne n'est en difficulté parce que le défaut a été contenu. d'atteindre la production.

La façon dont nous codons et la discipline qui peut être vue à partir de notre code peuvent aider les autres.


4

Une autre réponse canonique. Ceci, je crois, de Ron Jeffries:

Testez uniquement le code que vous souhaitez utiliser.


3

Tester le code standard est une perte de temps, mais comme le dit Slavo, si vous ajoutez un effet secondaire à vos getters / setters, vous devriez alors écrire un test pour accompagner cette fonctionnalité.

Si vous faites du développement piloté par les tests, vous devez d'abord écrire le contrat (par exemple, l'interface), puis écrire le (s) test (s) pour exercer cette interface qui documentent les résultats / comportements attendus. Ensuite, écrivez vos méthodes elles-mêmes, sans toucher au code de vos tests unitaires. Enfin, prenez un outil de couverture de code et assurez-vous que vos tests exercent tous les chemins logiques de votre code.


3

Un code vraiment trivial comme les getters et les setters qui n'ont pas de comportement supplémentaire que la définition d'un champ privé est excessif à tester. En 3.0 C # a même du sucre syntaxique où le compilateur s'occupe du champ privé pour que vous n'ayez pas à le programmer.

J'écris généralement beaucoup de tests très simples pour vérifier le comportement que j'attends de mes cours. Même si c'est simple comme l'ajout de deux nombres. Je bascule beaucoup entre l'écriture d'un simple test et l'écriture de quelques lignes de code. La raison en est que je peux alors changer de code sans avoir peur de casser des choses auxquelles je n'avais pas pensé.


Heureux que vous ayez fait un bon point sur le principe KISS .. J'ai souvent des tests qui sont littéralement comme 2-3 lignes de code, de vrais petits tests simples. Facile à grok et difficile à casser :) + 1'ed
Rob Cooper

3

Vous devriez tout tester. En ce moment, vous avez des getters et des setters, mais un jour vous pourriez les changer quelque peu, peut-être pour faire une validation ou autre chose. Les tests que vous écrivez aujourd'hui seront utilisés demain pour vous assurer que tout fonctionne comme d'habitude. Lorsque vous écrivez un test, vous devez oublier des considérations telles que «pour le moment, c'est trivial». Dans un contexte agile ou axé sur les tests, vous devez tester en supposant une refactorisation future. Aussi, avez-vous essayé de mettre des valeurs vraiment bizarres comme des chaînes extrêmement longues ou d'autres contenus "mauvais"? Eh bien, vous ne devriez jamais supposer à quel point votre code peut être abusé à l'avenir.

En général, je trouve qu'écrire de nombreux tests utilisateurs est d'un côté épuisant. D'un autre côté, cela vous donne toujours des informations précieuses sur le fonctionnement de votre application et vous aide à rejeter des hypothèses simples (et fausses) (comme: le nom d'utilisateur sera toujours inférieur à 1000 caractères).


3

Pour les modules simples qui peuvent se retrouver dans une boîte à outils, ou dans un type de projet open source, vous devez tester autant que possible, y compris les getters et setters triviaux. La chose que vous voulez garder à l'esprit est que générer un test unitaire lorsque vous écrivez un module particulier est assez simple et direct. L'ajout de getters et de setters est un code minimal et peut être géré sans trop de réflexion. Cependant, une fois que votre code est placé dans un système plus grand, cet effort supplémentaire peut vous protéger contre les modifications du système sous-jacent, telles que les modifications de type dans une classe de base. Tester tout est le meilleur moyen d'avoir une régression complète.


2

Cela ne fait pas de mal d'écrire des tests unitaires pour vos getters et setters. Pour le moment, il se peut qu'ils ne fassent qu'obtenir / ensembles de champs sous le capot, mais à l'avenir, vous pourriez avoir une logique de validation ou des dépendances entre propriétés qui doivent être testées. Il est plus facile de l'écrire maintenant pendant que vous y réfléchissez, puis de ne pas oublier de le moderniser si ce moment arrive.


eh bien, si vos getters / setters ont besoin de tests unitaires, il doit y avoir une logique qui leur est associée, ce qui signifie que la logique doit être écrite à l'intérieur d'eux, s'ils n'ont aucune logique, aucun test unitaire n'a besoin d'être écrit.
Pop Catalin

2
Son argument est que la logique peut y être ajoutée plus tard.
LegendLength

2

en général, lorsqu'une méthode n'est définie que pour certaines valeurs, tester les valeurs sur et au -delà de ce qui est acceptable. En d'autres termes, assurez-vous que votre méthode fait ce qu'elle est censée faire, mais rien de plus . Ceci est important, car lorsque vous allez échouer, vous voulez échouer tôt.

Dans les hiérarchies d'héritage, assurez-vous de tester LSP conformité .

Tester les getters et setters par défaut ne me semble pas très utile, sauf si vous prévoyez de faire une validation plus tard.


2

eh bien, si vous pensez qu'il peut casser, faites un test pour cela. Je ne teste généralement pas le setter / getter, mais disons que vous en faites un pour User.Name, qui concatène le prénom et le nom, j'écrirais un test, donc si quelqu'un change l'ordre pour le nom et le prénom, au moins il le saurait il a changé quelque chose qui a été testé.


2

La réponse canonique est "tester tout ce qui peut éventuellement casser". Si vous êtes sûr que les propriétés ne seront pas endommagées, ne les testez pas.

Et une fois que quelque chose s'est cassé (vous trouvez un bogue), cela signifie évidemment que vous devez le tester. Écrivez un test pour reproduire le bogue, observez-le échouer, puis corrigez le bogue, puis regardez le test passer.


1

Si je comprends bien les tests unitaires dans le contexte du développement agile, Mike, oui, vous devez tester les getters et les setters (en supposant qu'ils soient visibles publiquement). Tout le concept du test unitaire est de tester l'unité logicielle, qui est une classe dans ce cas, comme une boîte noire . Étant donné que les getters et les setters sont visibles de l'extérieur, vous devez les tester avec Authenticate et Save.


1

Si les méthodes d'authentification et d'enregistrement utilisent les propriétés, vos tests toucheront indirectement les propriétés. Tant que les propriétés ne fournissent qu'un accès aux données, des tests explicites ne devraient pas être nécessaires (sauf si vous optez pour une couverture à 100%).


1

Je testerais vos getters et vos setters. Selon la personne qui écrit le code, certaines personnes changent la signification des méthodes getter / setter. J'ai vu l'initialisation des variables et d'autres validations dans le cadre des méthodes getter. Afin de tester ce genre de chose, vous voudriez des tests unitaires couvrant ce code explicitement.


1

Personnellement, je "testerais tout ce qui peut casser" et un simple getter (ou même de meilleures propriétés automatiques) ne cassera pas. Je n'ai jamais eu d'échec d'une simple déclaration de retour et je n'ai donc jamais fait de test pour eux. Si les getters ont un calcul en leur sein ou une autre forme de déclaration, j'ajouterais certainement des tests pour eux.

Personnellement, j'utilise Moq comme une maquette d'objet, puis je vérifie que mon objet appelle les objets environnants comme il le devrait.


1

Vous devez couvrir l'exécution de chaque méthode de la classe avec UT et vérifier la valeur de retour de la méthode. Cela inclut les getters et les setters, en particulier dans le cas où les membres (propriétés) sont des classes complexes, ce qui nécessite une allocation de mémoire importante lors de leur initialisation. Appelez le setter avec une très grande chaîne par exemple (ou quelque chose avec des symboles grecs) et vérifiez que le résultat est correct (non tronqué, l'encodage est bon, etc.)

Dans le cas d'entiers simples qui s'appliquent également - que se passe-t-il si vous passez un long au lieu d'un entier? C'est la raison pour laquelle vous écrivez UT pour :)


1

Je ne testerais pas le paramètre réel des propriétés. Je serais plus préoccupé par la façon dont ces propriétés sont peuplées par le consommateur et par ce qu'elles les peuplent. Avec tout test, vous devez peser les risques avec le temps / coût des tests.


1

Vous devriez tester «chaque bloc de code non trivial» en utilisant autant que possible des tests unitaires.

Si vos propriétés sont triviales et qu'il est peu probable que quelqu'un y introduise un bogue, alors il devrait être prudent de ne pas les tester unitaire.

Vos méthodes Authenticate () et Save () semblent être de bons candidats pour les tests.


1

Idéalement, vous auriez effectué vos tests unitaires pendant que vous écriviez la classe. C'est ainsi que vous êtes censé le faire lorsque vous utilisez le développement piloté par les tests. Vous ajoutez les tests au fur et à mesure que vous implémentez chaque point de fonction, en vous assurant de couvrir également les cas limites avec test.

Ecrire les tests par la suite est beaucoup plus douloureux, mais faisable.

Voici ce que je ferais à votre place:

  1. Écrivez un ensemble de tests de base qui testent la fonction principale.
  2. Obtenez NCover et exécutez-le sur vos tests. Votre couverture de test sera probablement d'environ 50% à ce stade.
  3. Continuez à ajouter des tests qui couvrent vos bordures jusqu'à ce que vous obteniez une couverture d'environ 80% à 90%

Cela devrait vous donner un bel ensemble de tests unitaires qui agiront comme un bon tampon contre les régressions.

Le seul problème avec cette approche est que le code doit être conçu pour être testable de cette manière. Si vous avez commis des erreurs de couplage au début, vous ne pourrez pas obtenir une couverture élevée très facilement.

C'est pourquoi il est vraiment important d'écrire les tests avant d'écrire le code. Cela vous oblige à écrire du code faiblement couplé.


1

Ne testez pas le code (passe-partout) qui fonctionne manifestement. Donc, si vos setters et getters sont simplement "propertyvalue = value" et "return propertyvalue", cela n'a aucun sens de le tester.


1

Même get / set peut avoir des conséquences étranges, selon la manière dont ils ont été implémentés, ils doivent donc être traités comme des méthodes.

Chaque test de ceux-ci devra spécifier des ensembles de paramètres pour les propriétés, définissant à la fois des propriétés acceptables et inacceptables pour garantir que les appels retournent / échouent de la manière attendue.

Vous devez également être conscient des pièges de sécurité, par exemple d'injection SQL, et les tester.

Alors oui, vous devez vous soucier de tester les propriétés.


1

Je pense que c'est idiot de tester les getters et les setters lorsqu'ils ne font qu'une simple opération. Personnellement, je n'écris pas de tests unitaires complexes pour couvrir n'importe quel modèle d'utilisation. J'essaie d'écrire suffisamment de tests pour m'assurer que j'ai géré le comportement d'exécution normal et autant de cas d'erreur auxquels je peux penser. J'écrirai plus de tests unitaires en réponse aux rapports de bogues. J'utilise le test unitaire pour m'assurer que le code répond aux exigences et pour faciliter les futures modifications. Je me sens beaucoup plus disposé à changer de code quand je sais que si je casse quelque chose, un test échouera.


1

J'écrirais un test pour tout ce pour quoi vous écrivez du code qui est testable en dehors de l'interface graphique.

En règle générale, toute logique que j'écris qui a une logique métier que je place dans un autre niveau ou couche de logique métier.

Ensuite, il est facile d'écrire des tests pour tout ce qui fait quelque chose.

Tout d'abord, rédigez un test unitaire pour chaque méthode publique dans votre «couche de logique métier».

Si j'avais un cours comme celui-ci:

   public class AccountService
    {
        public void DebitAccount(int accountNumber, double amount)
        {

        }

        public void CreditAccount(int accountNumber, double amount)
        {

        }

        public void CloseAccount(int accountNumber)
        {

        }
    }

La première chose que je ferais avant d'écrire un code sachant que j'avais ces actions à effectuer serait de commencer à écrire des tests unitaires.

   [TestFixture]
    public class AccountServiceTests
    {
        [Test]
        public void DebitAccountTest()
        {

        }

        [Test]
        public void CreditAccountTest()
        {

        }

        [Test]
        public void CloseAccountTest()
        {

        }
    }

Écrivez vos tests pour valider le code que vous avez écrit pour faire quelque chose. Si vous parcourez une collection de choses et que vous changez quelque chose à propos de chacun d'eux, écrivez un test qui fait la même chose et affirmez ce qui s'est réellement passé.

Il existe de nombreuses autres approches que vous pouvez adopter, à savoir le développement piloté par le comportement (BDD), qui est plus complexe et n'est pas un bon point de départ pour vos compétences en tests unitaires.

Donc, la morale de l'histoire est de tester tout ce qui fait tout ce qui vous inquiète, de faire en sorte que les tests unitaires testent des choses spécifiques de petite taille, beaucoup de tests sont bons.

Gardez votre logique métier en dehors de la couche d'interface utilisateur afin de pouvoir facilement écrire des tests pour eux, et vous serez bon.

Je recommande TestDriven.Net ou ReSharper car les deux s'intègrent facilement dans Visual Studio.


1

Je recommanderais d'écrire plusieurs tests pour vos méthodes Authenticate et Save. En plus du cas de réussite (où tous les paramètres sont fournis, tout est correctement orthographié, etc.), il est bon d'avoir des tests pour différents cas d'échec (paramètres incorrects ou manquants, connexions de base de données non disponibles le cas échéant, etc.). Je recommande Pragmatic Unit Testing en C # avec NUnit comme référence.

Comme d'autres l'ont indiqué, les tests unitaires pour les getters et les setters sont exagérés, à moins qu'il n'y ait une logique conditionnelle dans vos getters et setters.


1

Bien qu'il soit possible de deviner correctement où votre code doit être testé, je pense généralement que vous avez besoin de métriques pour confirmer cette hypothèse. À mon avis, les tests unitaires vont de pair avec les métriques de couverture de code.

Code avec beaucoup de tests mais une petite couverture n'a pas été bien testé. Cela dit, le code avec une couverture à 100% mais ne testant pas les cas de limite et d'erreur n'est pas non plus génial.

Vous voulez un équilibre entre une couverture élevée (90% minimum) et des données d'entrée variables.

N'oubliez pas de tester les «déchets»!

De plus, un test unitaire n'est pas un test unitaire à moins qu'il ne vérifie un échec. Les tests unitaires qui n'ont pas d'assertions ou qui sont marqués avec des exceptions connues testeront simplement que le code ne meurt pas lorsqu'il est exécuté!

Vous devez concevoir vos tests pour qu'ils signalent toujours les échecs ou les données inattendues / indésirables!


1

Cela rend notre code meilleur ... point final!

Une chose que nous, les développeurs de logiciels, oublions quand nous faisons du développement piloté par les tests, c'est le but de nos actions. Si un test unitaire est en cours d'écriture après que le code de production est déjà en place, la valeur du test diminue considérablement (mais n'est pas complètement perdue).

Dans le véritable esprit des tests unitaires, ces tests ne sont pas principalement là pour "tester" davantage notre code; ou pour obtenir une meilleure couverture du code de 90% à 100%. Ce sont tous des avantages marginaux à rédiger d'abord les tests. Le gros avantage est que notre code de production est bien mieux écrit grâce au processus naturel de TDD.

Pour aider à mieux communiquer cette idée, les éléments suivants peuvent être utiles à la lecture:

La théorie erronée des tests
unitaires Développement de logiciels ciblés

Si nous pensons que le fait d'écrire plus de tests unitaires est ce qui nous aide à obtenir un produit de meilleure qualité, alors nous pouvons souffrir d'un culte du fret du développement axé sur les tests.


Je ne suis pas d'accord avec l'affirmation selon laquelle les tests unitaires n'ont pas de valeur une fois que le code de production est déjà en place. De telles assertions ne tiennent pas compte de leur utilité dans la réplication des conditions d'erreur trouvées en production ou dans la compréhension du code hérité d'un développeur ou d'une équipe précédente.
Scott Lawrence

J'ai peut-être mal rencontré. Je ne voulais pas dire que les tests unitaires n'ont pas de valeur une fois que le code de production est en place. Cependant, leur valeur diminue. Le plus grand avantage des tests unitaires vient de la magie inhérente qui se produit lorsque nous les laissons conduire notre développement de production.
Scott Saad
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.