Meilleures pratiques de développement piloté par les tests utilisant C # et RhinoMocks [fermé]

StackOverflow https://stackoverflow.com/questions/124210

Question

Afin d’aider mon équipe à écrire du code testable, j’ai dressé cette liste simple des meilleures pratiques permettant de rendre notre base de code C # plus testable. (Certains points font référence aux limitations de Rhino Mocks, un cadre moqueur pour C #, mais les règles peuvent également s'appliquer de manière plus générale.) Quelqu'un a-t-il déjà appliqué les meilleures pratiques?

Pour optimiser la testabilité du code, suivez ces règles:

  1. Écrivez le test en premier, puis le code. Raison: cela garantit que vous écrivez du code pouvant être testé et que chaque ligne de code reçoit les tests qui y sont écrits.

  2. Les classes de conception utilisant l'injection de dépendances. Raison: vous ne pouvez pas vous moquer ni tester ce qui ne peut pas être vu.

  3. Séparez le code de l'interface utilisateur de son comportement à l'aide de Model-View-Controller ou de Model-View-Presenter. Raison: permet à la logique métier d'être testée, tandis que les pièces ne pouvant pas être testées. (l'interface utilisateur) est réduite au minimum.

  4. N'écrivez pas de méthodes ni de classes statiques. Raison: les méthodes statiques sont difficiles, voire impossibles à isoler, et Rhino Mocks est incapable de les imiter.

  5. Programmez des interfaces et non des classes. Raison: l'utilisation d'interfaces permet de clarifier les relations entre les objets. Une interface doit définir un service dont un objet a besoin de son environnement. De plus, les interfaces peuvent être facilement simulées à l’aide de Rhino Mocks et d’autres frameworks moqueurs.

  6. Isolez les dépendances externes. Raison: les dépendances externes non résolues ne peuvent pas être testées.

  7. Marquez comme virtuelles les méthodes que vous avez l'intention de simuler. Raison: Rhino Mocks ne parvient pas à simuler des méthodes non virtuelles.

Était-ce utile?

La solution

Certainement une bonne liste. Voici quelques réflexions à ce sujet:

  

Écrivez le test en premier, puis le code.

Je suis d'accord, à un niveau élevé. Mais, je serais plus précis: "Commencez par écrire un test, puis écrivez juste assez de code pour réussir le test, puis recommencez." Sinon, j’aurais peur que mes tests unitaires ressemblent davantage à des tests d’intégration ou d’acceptation.

  

Les classes de conception utilisant l'injection de dépendances.

D'accord. Lorsqu'un objet crée ses propres dépendances, vous ne les contrôlez pas. L'inversion de l'injection de contrôle / dépendance vous donne ce contrôle, ce qui vous permet d'isoler l'objet à tester avec des simulacres / stubs / etc. C’est ainsi que vous testez des objets de manière isolée.

  

Séparez le code de l'interface utilisateur de son comportement à l'aide de Model-View-Controller ou de Model-View-Presenter.

D'accord. Notez que même le présentateur / contrôleur peut être testé avec DI / IoC, en lui donnant une vue et un modèle stubbed / mocked. Consultez Présentateur d'abord pour plus d'informations sur ce sujet.

  

N'écrivez pas de méthodes ni de classes statiques.

Je ne suis pas sûr d'être d'accord avec celui-ci. Il est possible de tester à l'unité une méthode / classe statique sans utiliser de mock. C’est donc peut-être une de ces règles spécifiques à Rhino Mock que vous avez mentionnées.

  

Programmez les interfaces et non les classes.

Je suis d'accord, mais pour une raison légèrement différente. Les interfaces offrent aux développeurs de logiciels une grande flexibilité - au-delà du simple support de divers cadres d’objets fictifs. Par exemple, il n'est pas possible de prendre en charge correctement l'ID sans interfaces.

  

Isolez les dépendances externes.

D'accord. Cachez les dépendances externes derrière votre propre façade ou adaptateur (le cas échéant) avec une interface. Cela vous permettra d'isoler votre logiciel de la dépendance externe, que ce soit un service Web, une file d'attente, une base de données ou autre. Ceci est particulièrement important lorsque votre équipe ne contrôle pas la dépendance (a.k.a. external).

  

Marquez comme virtuelles les méthodes que vous avez l'intention de simuler.

C'est une limitation de Rhino Mocks. Dans un environnement qui préfère les stubs codés à la main sur un framework d'objet factice, cela ne serait pas nécessaire.

Et quelques nouveaux points à considérer:

Utilisez des modèles de conception créatifs. Cela vous aidera avec l'identification directe, mais vous permettra également d'isoler ce code et de le tester indépendamment de toute autre logique.

Rédigez des tests à l'aide de Arrangement / Act / de Bill Wake Technique d’affirmation . Cette technique indique très clairement quelle configuration est nécessaire, ce qui est réellement testé et ce qui est attendu.

N'ayez pas peur de créer vos propres mocs / stubs. Vous constaterez souvent qu'en utilisant des frameworks d'objets mock, vos tests sont extrêmement difficiles à lire. En faisant rouler les vôtres, vous aurez un contrôle total sur vos modèles / souches et pourrez garder vos tests lisibles. (Reportez-vous au point précédent.)

Évitez la tentation de refactoriser la duplication de vos tests unitaires en classes de base abstraites, ou en méthodes d'installation / de démontage. Cela masque le code de configuration / nettoyage du développeur qui tente de lancer le test unitaire. . Dans ce cas, la clarté de chaque test est plus importante que de refactoriser la duplication.

Implémentation de l'intégration continue. Enregistrez votre code sur chaque "barre verte". Construisez votre logiciel et exécutez votre suite complète de tests unitaires à chaque enregistrement. (Bien sûr, ce n'est pas une pratique de codage en soi; mais c'est un outil incroyable pour garder votre logiciel propre et entièrement intégré.)

Autres conseils

Si vous travaillez avec .Net 3.5, vous pouvez consulter la Moq . - il utilise des arbres d’expression et des lambdas pour supprimer les idiomes non intuitifs d’enregistrement-réponse de la plupart des autres bibliothèques moqueuses.

Découvrez ce quickstart pour voir à quel point vos scénarios de test deviennent plus intuitifs, voici un exemple simple:

// ShouldExpectMethodCallWithVariable
int value = 5;
var mock = new Mock<IFoo>();

mock.Expect(x => x.Duplicate(value)).Returns(() => value * 2);

Assert.AreEqual(value * 2, mock.Object.Duplicate(value));

Découvrez la différence entre les faux, les imitations et les talons et le moment de les utiliser.

Évitez de trop spécifier les interactions en utilisant des simulacres. Cela rend les tests fragiles .

Ceci est un article très utile!

J'ajouterais qu'il est toujours important de comprendre le contexte et le système à tester (SUT). Il est beaucoup plus facile de suivre les principes TDD à la lettre lorsque vous écrivez un nouveau code dans un environnement où le code existant suit les mêmes principes. Toutefois, lorsque vous écrivez du nouveau code dans un environnement hérité non-TDD, vous constatez que vos efforts en matière de TDD peuvent rapidement donner des résultats bien supérieurs à vos estimations et attentes.

Pour certains d'entre vous, qui vivons dans un monde entièrement académique, les délais et la livraison ne sont peut-être pas importants, mais dans un environnement où les logiciels représentent de l'argent, il est essentiel d'utiliser efficacement vos efforts de TDD.

TDD est fortement soumis à la loi de la Diminution du rendement marginal . En résumé, vos efforts en faveur du TDD sont de plus en plus utiles tant que vous n’atteignez pas un point de rendement maximal, après quoi, le temps investi ultérieurement dans le TDD aura de moins en moins de valeur.

J'ai tendance à croire que la principale valeur de TDD réside dans les limites (boîtes noires) ainsi que dans les tests occasionnels par des boîtes blanches des zones critiques du système.

La véritable raison de la programmation par rapport aux interfaces n'est pas pour simplifier la vie de Rhino, mais pour clarifier les relations entre les objets dans le code. Une interface doit définir un service dont un objet a besoin de son environnement. Une classe fournit une implémentation particulière de ce service. Lire Rebecca Wirfs-Brock " Object Design " livre sur les rôles, les responsabilités et les collaborateurs.

Bonne liste. Une des choses que vous voudrez peut-être établir - et je ne peux pas vous donner beaucoup de conseils car je commence tout juste à y penser moi-même - est le moment où une classe devrait se trouver dans une bibliothèque, un espace de noms, des espaces de noms imbriqués différents. Vous voudrez peut-être même établir une liste de bibliothèques et d'espaces de noms à l'avance et demander à l'équipe de se rencontrer et de décider de fusionner deux / d'ajouter un nouveau.

Oh, je viens de penser à quelque chose que je fais et que vous voudrez peut-être aussi. J'ai généralement une bibliothèque de tests unitaires avec une politique de test de montage par classe où chaque test va dans un espace de noms correspondant. J'ai aussi tendance à avoir une autre bibliothèque de tests (tests d'intégration?) Qui est plus style BDD . Cela me permet d’écrire des tests pour spécifier ce que la méthode devrait faire et ce que l’application devrait faire globalement.

Voici un autre projet auquel j'ai pensé que j'aime faire.

Si vous envisagez d'exécuter des tests à partir de l'interface utilisateur de test d'unité plutôt que de TestDriven.Net ou de NAnt, il est plus facile de définir le type de projet de test d'unité sur l'application console plutôt que sur la bibliothèque. Cela vous permet d'exécuter des tests manuellement et de les parcourir en mode débogage (ce que le site susmentionné TestDriven.Net peut réellement faire pour vous).

De plus, j'aime toujours avoir un projet Playground ouvert pour tester des morceaux de code et des idées avec lesquelles je ne suis pas familier. Cela ne doit pas être vérifié dans le contrôle de source. Mieux encore, il devrait figurer dans un référentiel de contrôle de source distinct sur la machine du développeur uniquement.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top