web-dev-qa-db-fra.com

Comment savez-vous quoi tester lors de l'écriture de tests unitaires?

En utilisant C #, j'ai besoin d'une classe appelée User qui a un nom d'utilisateur, un mot de passe, un indicateur 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 simplement écrire un test pour les méthodes? Et dois-je même me soucier de tester les propriétés, car ce sont les getter et les setters de .Net?

127
Mike Roosa

Ma question a également suscité de nombreuses réponses intéressantes: " Début TDD - Défis? Solutions? Recommandations? "

Permettez-moi également de jeter un coup d'œil à mon message de blog (qui s'inspire en partie de ma question). J'ai de bons retours à ce sujet. À savoir:

Je ne sais pas par où commencer?

  • Recommence. Ne pensez à écrire des tests que lorsque vous écrivez un nouveau code. Cela peut être une réutilisation d’ancien code ou une toute nouvelle fonctionnalité.
  • Commencez simple. Ne vous précipitez pas pour essayer de vous faire une idée de la structure de test et de la technologie TDD. Debug.Assert fonctionne bien. Utilisez-le comme point de départ. Cela ne gêne pas votre projet et ne crée pas de dépendances.
  • Commencez positif. Vous essayez d'améliorer votre métier, sentez-vous bien. J'ai vu beaucoup de développeurs 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 commencer à faire des tests. Attendez-vous à un défi, mais souvenez-vous - les défis peuvent être surmontés.

Ne testez que ce que vous attendez

J'ai eu de vrais problèmes au début, car j'étais constamment assis à essayer de résoudre tous les problèmes possibles, puis à essayer de le résoudre et de le corriger. C'est un moyen rapide pour un mal de tête. Les tests doivent être un vrai processus YAGNI. Si vous savez qu'il y a un problème, écrivez un test à ce sujet. Sinon, ne vous embêtez pas.

Seulement tester une chose

Chaque cas de test ne devrait jamais tester qu'une seule chose. Si vous vous retrouvez un jour avec "et" dans le nom du test, vous faites quelque chose de mal.

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

130
Rob Cooper

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 existe une probabilité non nulle que votre méthode instanceof ne fonctionne pas.

Ne testez pas des éléments que vous pouvez compter sur le langage à appliquer. Dans votre cas, je me concentrerais sur vos méthodes d’authentification et d’enregistrement - et j’écrirais des tests pour s’assurer qu’elles pouvaient gérer les valeurs NULL dans l’un ou l’ensemble de ces champs.

63
Tim Howland

Cela m'a amené aux tests unitaires et cela m'a fait très plaisir

Nous venons juste de commencer à faire des tests unitaires. Pendant longtemps, j'ai su que ce serait bien de commencer à le faire, mais je ne savais pas comment commencer ni, 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 beaucoup de 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à entrées dans le système de comptabilité.

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

J'ai donc eu un problème très complexe à résoudre avec de nombreuses possibilités qu'un scénario fonctionne parfaitement mais se trompe avec un autre type de combinaison invocie/payment.

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

J'ai commencé à écrire (dans le 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 entre ces informations via une interface utilisateur.

Ensuite, j'ai créé le premier TestMethod, testant un paiement très simple d'une facture unique sans aucune réduction 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 ce qui devrait être les bons numéros se retrouvant dans la transaction bancaire et dans la facture liée. Je vérifie le nombre de paiements, le montant des paiements, le montant de la remise et le solde de la facture après la transaction.

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

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

J'ai fait le premier test, corrigé un petit bug jusqu'à la réussite de mon test.

Ensuite, j'ai commencé à écrire le deuxième test, cette fois avec une réduction de paiement. Après avoir écrit le test, j'ai modifié le mode de paiement pour prendre en charge les remises.

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

Ensuite, j'ai travaillé sur des scénarios plus complexes.

1) Pensez à un nouveau scénario

2) Écrire un test pour ce scénario

3) Exécutez ce seul test 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 mode de paiement très complexe. Sans tests unitaires, je ne savais pas comment commencer à coder, le problème semblait accablant. Avec les tests, je pouvais commencer avec une méthode simple et l'étendre étape par étape avec l'assurance que les scénarios les plus simples continueraient à fonctionner.

Je suis sûr que les tests unitaires m'ont permis de gagner quelques jours (ou quelques semaines) de codage et de garantir 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 tout en m'assurant que les autres scénarios fonctionnent toujours correctement. Cela économisera des jours et des jours dans la phase de maintenance et de correction des bogues.

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

Vous trouverez ci-dessous 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);
    }
38
eroijen

S'ils sont vraiment triviaux, alors ne vous inquiétez pas des tests. Par exemple, si elles sont mises en œuvre comme ça;

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

Si, par contre, vous faites quelque chose d'intelligent (comme chiffrer et déchiffrer le mot de passe dans le getter/setter), testez-le.

13
Steve Cooper

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'affectent que des valeurs à certains champs privés, ne vous inquiétez pas.

10
Slavo

Cette question semble être une question de savoir où on peut tracer la ligne sur quelles méthodes sont testées et lesquelles ne le sont pas.

Les paramètres et les accesseurs pour la valorisation ont été créés dans un souci de cohérence et de croissance future, et ils prévoient qu’à l’avenir, le configurateur/acquéreur 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.

L'objectif principal est la fiabilité du code, en particulier lors de modifications visant à ajouter des fonctionnalités supplémentaires. Je ne suis au courant de personne qui se soit fait virer pour avoir inclus des setters/getters dans la méthodologie de test, mais je suis certain plus le cas.

Peut-être un autre membre de l'équipe a-t-il développé les méthodes set/get pour inclure une logique qui doit maintenant être testée mais 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. Les tests que vous effectuez en développement et en assurance qualité ne déclenchent pas le problème, mais les données commerciales réelles du premier jour de publication ne déclenche le.

Les deux coéquipiers vont maintenant débattre pour savoir qui a lâché la balle et qui n’a pas réussi à faire des tests unitaires lorsque le set/se métamorphose pour inclure une logique qui peut échouer mais qui n’est pas couverte par un test unitaire. Le coéquipier qui a initialement écrit le set/gets aura plus de facilité à sortir de ce nettoyage si les tests ont été mis en œuvre dès le premier jour sur le set/gets simple.

Mon opinion est que quelques minutes de "temps perdu" à couvrir TOUTES les méthodes avec des tests unitaires, même les plus triviaux, pourraient vous épargner des jours de maux de tête, de perte d'argent/de réputation de l'entreprise et de perte du travail de quelqu'un.

Et le fait que vous ayez associé des méthodes triviales à des tests unitaires peut être perçu par ce coéquipier junior qui modifie les méthodes triviales en méthodes non triviales et les invite à mettre à jour le test. d'atteindre la production.

La façon dont nous codons et la discipline qui ressort de notre code peuvent aider les autres.

6
Thomas Carlisle

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

Ne testez que le code que vous souhaitez utiliser.

4
user9397

Un code vraiment trivial, tel que les getters et les setters qui n'ont pas de comportement supplémentaire par rapport à la définition d'un champ privé, est excessif à tester. Dans la version 3.0, C # a même un sucre syntaxique dans lequel le compilateur prend en charge le champ privé, ce qui vous évite de le programmer.

J'écris habituellement beaucoup de tests très simples pour vérifier le comportement que j'attends de mes cours. Même si c'est simple, comme ajouter deux nombres. Je change beaucoup entre écrire un simple test et écrire des lignes de code. La raison en est que je peux alors changer de code sans craindre d'avoir cassé des choses auxquelles je n'avais pas pensé.

3
Mendelt

Vous devriez tout tester. Actuellement, vous avez des accesseurs et des passeurs, mais un jour, vous pourrez les modifier quelque peu, par exemple pour valider ou autre chose. Les tests que vous écrivez aujourd'hui seront utilisés demain pour s'assurer que tout continue à fonctionner normalement. Lorsque vous écrivez un test, vous devriez 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. En outre, avez-vous essayé de mettre en place des valeurs vraiment étranges telles que des chaînes extrêmement longues ou tout autre contenu "mauvais"? Eh bien, vous devriez ... ne jamais présumer à quel point votre code peut être mal utilisé à l'avenir.

De manière générale, je trouve qu'écrire des tests utilisateur volumineux est épuisant. D'un autre côté, même si cela vous donne toujours des informations précieuses sur le fonctionnement de votre application et vous permet de jeter de simples suppositions (et fausses) (comme: le nom d'utilisateur sera toujours inférieur à 1000 caractères).

3
Sklivvz

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 les setters triviaux. Ce que vous voulez garder à l'esprit, c'est que générer un test unitaire lors de l'écriture d'un module particulier est assez simple et direct. L'ajout de getters et de setters est un code minimal et peut être manipulé sans trop y penser. 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.

3
Dirigible

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

Si vous effectuez un développement piloté par des tests, vous devez d’abord rédiger le contrat (par exemple, une interface), puis rédiger le ou les tests permettant d’exercer cette interface et documentant les résultats/comportements attendus. Alors écrivez vos méthodes elles-mêmes, sans toucher au code dans vos tests unitaires. Enfin, saisissez un outil de couverture de code et assurez-vous que vos tests utilisent toutes les voies logiques de votre code.

3
warren_s

en général, lorsqu'une méthode n'est définie que pour certaines valeurs, testez les valeurs sur et sur la limite de ce qui est acceptable. En d'autres termes, assurez-vous que votre méthode fait ce qu'elle est supposé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 vérifier la conformité LSP .

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

2
Rik

eh bien, si vous pensez que cela peut casser, écrivez un test pour cela. Habituellement, je ne teste pas le getter/getter, mais disons que vous en créez un pour User.Name, qui concatène le nom et le prénom, j'écrirais un test afin que si quelqu'un modifie l'ordre pour le nom et le prénom, au moins il le saurait il a changé quelque chose qui a été testé.

2
pmlarocque

La réponse canonique est "testez tout ce qui peut éventuellement casser". Si vous êtes certain que les propriétés ne casseront pas, ne les testez pas.

Et une fois que quelque chose s’est avéré cassé (vous trouvez un bogue), cela signifie évidemment que vous devez le tester. Ecrivez un test pour reproduire le bogue, observez son échec, puis corrigez le bogue, puis regardez le test passer.

2
Eric Normand

Il ne fait pas de mal d'écrire des tests unitaires pour vos getters et setters. À l’heure actuelle, ils ne font peut-être que des recherches sur le terrain, mais à l’avenir, vous aurez peut-être une logique de validation ou des dépendances entre propriétés qui devront être testées. Il est plus facile de l'écrire maintenant pendant que vous y réfléchissez puis de vous souvenir de l'adapter ultérieurement, si jamais cela se produisait.

2
Bob King

Idéalement, vous auriez fait vos tests unitaires en écrivant le cours. C’est ce que vous êtes censé 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 Edge avec le test.

Écrire les tests après est beaucoup plus pénible, mais faisable.

Voici ce que je ferais dans votre position:

  1. Rédigez 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 autour de 50% à ce stade.
  3. Continuez à ajouter des tests couvrant vos cas Edge jusqu'à ce que vous obteniez une couverture d'environ 80% à 90%

Cela devrait vous donner un ensemble de tests unitaires de Nice fonctionnant bien qui agira comme un bon tampon contre les régressions.

Le seul problème avec cette approche est que le code doit être conç pour pouvoir être testé de cette manière. Si vous avez commis des erreurs de couplage très tôt, 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
Simon Johnson

Cela rend notre code meilleur ... point!

Une chose que les développeurs de logiciels oublient lorsqu’un développement fondé sur des tests est l’objet de nos actions Si un test unitaire est écrit alors 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 sont et non principalement là pour "tester" davantage de notre code; ou pour obtenir une couverture de code supérieure de 90% à 100%. Ce sont tous des avantages marginaux de la rédaction des tests en premier. Le gros avantage est que notre code de production finit par être écrit beaucoup mieux en raison du processus naturel du TDD.

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

Théorie imparfaite des tests unitaires
Développement de logiciel intentionnel

Si nous estimons que le fait d’écrire plus de tests unitaires est ce qui nous aide à obtenir un produit de meilleure qualité, il se peut que nous souffrions d’un Cargo Cult du développement piloté par les tests.

1
Scott Saad

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

1
erlando

Même obtenir/définir peut avoir des conséquences étranges, en fonction de la façon dont elles ont été mises en œuvre, elles doivent donc être traitées comme des méthodes.

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

Vous devez également connaître les pièges de la sécurité, comme exemple d'injection SQL, et les tester.

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

1
CestLaGalere

Je pense que c'est idiot de tester des getters et des setters quand ils ne font qu'une simple opération. Personnellement, je n’écris pas de tests unitaires complexes pour couvrir tous les types 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 que je peux penser. J'écrirai plus de tests unitaires en réponse aux rapports de bugs. J'utilise des tests unitaires pour vérifier que le code répond aux exigences et faciliter les modifications futures. Je me sens beaucoup plus disposé à changer de code quand je sais que si je casse quelque chose, le test échouera.

1
Andrei Savu

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 sauvegarder cette estimation. À mon avis, les tests unitaires vont de pair avec les mesures de couverture de code.

Code avec beaucoup de tests mais une petite couverture n'a pas été bien testée. Cela dit, un code avec une couverture de 100% mais ne testant pas les cas de délimitation et d'erreur n'est également pas génial.

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

N'oubliez pas de tester "garbage in"!

En outre, un test unitaire n'est pas un test unitaire sauf s'il recherche une défaillance. Les tests unitaires sans assertions ou marqués avec des exceptions connues vérifieront simplement que le code ne meurt pas lorsqu'il est exécuté!

Vous devez concevoir vos tests de manière à ce qu'ils signalent toujours des échecs ou des données inattendues/indésirables!

1
Ray Hayes

Je voudrais écrire un test pour tout ce que vous écrivez du code pour cela est testable en dehors de l'interface graphique.

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

Ensuite, écrire des tests pour tout ce qui fait quelque chose est facile à faire.

Tout d’abord réussi, écrivez un test unitaire pour chaque méthode publique dans votre "couche de logique applicative".

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 à faire avant d'écrire un code, sachant que j'avais ces actions à effectuer, était de commencer à écrire des tests unitaires.

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

        }

        [Test]
        public void CreditAccountTest()
        {

        }

        [Test]
        public void CloseAccountTest()
        {

        }
    }

Ecrivez vos tests pour valider le code que vous avez écrit pour faire quelque chose. Si vous parcourez une collection de choses et changez quelque chose à chacune d’elles, écrivez un test qui fait la même chose et affirmez que cela s’est réellement passé.

Il existe de nombreuses autres approches que vous pouvez adopter, à savoir le développement dirigé par le comportement (BDD), qui est plus impliquée et qui ne constitue pas un excellent point de départ pour vos compétences en matière de tests unitaires.

Ainsi, la morale de l'histoire est la suivante: testez tout ce qui peut vous inquiéter, maintenez les tests unitaires pour tester des objets spécifiques de petite taille. De nombreux tests sont bons.

Conservez votre logique métier en dehors de la couche Interface utilisateur afin de pouvoir facilement écrire des tests, et tout ira bien.

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

1
Dean Poulin

Si je comprends bien les tests unitaires dans le contexte du développement agile, Mike, oui, vous devez tester les accesseurs et les passeurs (en supposant qu’ils soient visibles publiquement). Le concept de test unitaire consiste à tester le logiciel, qui est une classe dans ce cas, en tant que boîte noire . Comme les accesseurs et les setters sont visibles de l'extérieur, vous devez les tester avec Authentifier et enregistrer.

1
Onorio Catenacci

Si les méthodes Authentifier et Enregistrer utilisent les propriétés, vos tests toucheront indirectement les propriétés. Tant que les propriétés fournissent uniquement un accès aux données, des tests explicites ne devraient pas être nécessaires (à moins que vous n'ayez une couverture à 100%).

1
Tom Walker

Je voudrais tester vos getters et setters. Selon l'auteur du code, certaines personnes modifient le sens des méthodes getter/setter. J'ai vu l'initialisation de 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 explicitement ce code.

1
Peter Bernier

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

Personnellement, j'utilise Moq comme structure d'objet fictif, puis je vérifie que mon objet appelle les objets environnants comme il se doit.

1
tronda

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 si les membres (propriétés) sont des classes complexes, ce qui nécessite grande quantité de mémoire allouée lors de leur initialisation.Appelez le setter avec une très grosse 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, cela s'applique également - qu'arrive-t-il si vous passez long au lieu d'entier? C'est la raison pour laquelle vous écrivez UT pour :)

1
m_pGladiator

Le test d'une classe doit vérifier que:

  1. méthodes et propriétés retournent les valeurs attendues
  2. Les exceptions appropriées sont levées lorsqu'un argument non valide est fourni
  3. Les interactions entre la classe et d'autres objets se produisent comme prévu lorsqu'une méthode donnée est appelée

Bien sûr, si les getters et les setters n'ont pas de logique particulière, les tests des méthodes Authenticate andSave devraient les couvrir, mais sinon, un test explicite devrait être écrit.

1
Crippledsmurf

Je recommanderais d'écrire plusieurs tests pour vos méthodes d'authentification et d'enregistrement. En plus du cas de réussite (où tous les paramètres sont fournis, tout est correctement orthographié, etc.), il est bon de disposer de tests pour divers cas d'échec (paramètres incorrects ou manquants, connexions à la base de données non disponibles, le cas échéant, etc.). Je recommande Test d'unité pragmatique en C # avec NUnit comme référence.

Comme d'autres l'ont indiqué, les tests unitaires pour les getters et les setters sont excessifs, à moins que vos getters et setters ne comportent une logique conditionnelle.

1
Scott Lawrence

Je ne testerais pas le paramétrage 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 quoi elles sont peuplées. Avec tout test, vous devez peser les risques avec le temps/coût des tests.

1
Bloodhound

Vous devriez tester "chaque bloc de code non trivial" en utilisant autant que possible les tests unitaires.

Si vos propriétés sont triviales et qu'il est peu probable que quelqu'un y introduise un bogue, il est prudent de ne pas les tester à l'unité.

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

1
user7015

Écrire du code sans valeur est toujours une mauvaise idée. Puisque le test proposé n’ajoute aucune valeur à votre projet (ou très proche de celui-ci). Ensuite, vous perdez un temps précieux que vous pourriez passer à écrire du code qui apporte réellement de la valeur.

0
pjesi

Je seconde teste tout ce qui peut éventuellement casser et n'écris pas de tests idiots. Mais le principe le plus important est testez tout ce que vous trouvez est cassé: si une méthode se comporte bizarrement, écrivez un test pour décrire le jeu de données qui le fait échouer, puis corrigez le bogue et regardez la barre devenir verte. Testez également les valeurs de données "limites" (null, 0, MAX_INT, listes vides, etc.).

0
Manrico Corazzi

Lorsque vous écrivez des tests unitaires, ou réellement des tests, vous déterminez quoi tester en regardant les conditions aux limites de ce que vous testez. Par exemple, vous avez une fonction appelée is_prime. Heureusement, il fait ce que son nom implique et vous indique si l'objet entier est premier ou non. Pour cela, je suppose que vous utilisez des objets. Nous devons maintenant vérifier que des résultats valables sont obtenus pour une plage connue d'objets premiers et non premiers. C'est ton point de départ.

Essentiellement, regardez ce qui devrait se passer avec une fonction, une méthode, un programme ou un script, puis ce qui devrait absolument se produire pas avec ce même code. C'est la base de votre test. Préparez-vous simplement à modifier vos tests au fur et à mesure que vous maîtrisez mieux ce que devrait) == avec votre code.

0
Dan

La meilleure règle empirique que j'ai vue est de tester tout ce que vous ne pouvez pas dire en un coup d'œil, pour sûr, fonctionnera correctement. Rien de plus et vous finissez par tester la langue/l'environnement.

0
user74754