112 votes

Comment tester les méthodes privées avec NUnit ?

Je me demande comment utiliser NUnit correctement. Tout d'abord, j'ai créé un projet de test séparé qui utilise mon projet principal comme référence. Mais dans ce cas, je ne suis pas en mesure de tester les méthodes privées. J'ai pensé que je devais inclure mon code de test dans mon code principal ! - Cela ne semble pas être la bonne façon de procéder. (Je n'aime pas l'idée d'expédier du code avec des tests dedans).

Comment tester les méthodes privées avec NUnit ?

74voto

harpo Points 17399

En général, les tests unitaires portent sur l'interface publique d'une classe, en partant du principe que l'implémentation est sans importance, tant que les résultats sont corrects du point de vue du client.

Ainsi, NUnit ne fournit pas de mécanisme pour tester les membres non publics.

0 votes

Vous avez tout à fait raison. S'il y a une logique privée qui doit être testée de toute urgence (à cause des bugs de frequnet), alors je devrais penser à des décompositions plutôt qu'à tester des circuits internes.

1 votes

+1 Je viens de me heurter à ce problème et dans mon cas, il y a un algorithme de "mapping" qui se produit entre le privé et le public, ce qui signifie que si je devais faire un test unitaire du public, ce serait en fait un test d'intégration. Dans ce scénario, je pense que le fait d'essayer d'écrire le test indique un problème de conception dans le code. Dans ce cas, je devrais probablement créer une classe différente qui exécute cette méthode "privée".

0 votes

@MrFox en fait toute la logique privée est exposée au monde extérieur par les méthodes publiques, donc éventuellement vous pouvez couvrir tout cela par des tests pour l'API publique de votre classe. Cependant, le découplage peut être une meilleure option si la logique privée ajoute trop de complexité cyclomatique à votre classe.

69voto

user1039513 Points 154

Si je suis d'accord pour dire que les tests unitaires doivent se concentrer sur l'interface publique, vous obtenez une impression bien plus granulaire de votre code si vous testez également les méthodes privées. Le cadre de test MS permet cela grâce à l'utilisation de PrivateObject et PrivateType, mais pas NUnit. Ce que je fais à la place est :

private MethodInfo GetMethod(string methodName)
{
    if (string.IsNullOrWhiteSpace(methodName))
        Assert.Fail("methodName cannot be null or whitespace");

    var method = this.objectUnderTest.GetType()
        .GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);

    if (method == null)
        Assert.Fail(string.Format("{0} method not found", methodName));

    return method;
}

De cette façon, vous ne devez pas compromettre l'encapsulation en faveur de la testabilité. Gardez à l'esprit que vous devrez modifier vos BindingFlags si vous souhaitez tester des méthodes statiques privées. L'exemple ci-dessus ne concerne que les méthodes d'instance.

1 votes

En testant les méthodes privées, il semble que vous rendiez plus difficile l'accomplissement du cycle TDD complet : rouge->vert->réfactor. Si je dois sans cesse modifier mes tests pour refactoriser, je perds du temps et je ne suis pas sûr de pouvoir faire réellement confiance à ces tests. Je sais que tous ceux qui écrivent des tests unitaires n'utilisent pas le TDD, mais vous causez quand même plus de problèmes lorsque vous devez procéder à un remaniement.

0 votes

Pouvez-vous donner un exemple de modification de vos tests ? Je ne comprends pas votre point de vue.

7 votes

Tester ces petites méthodes d'aide permet de capturer la partie unitaire des tests unitaires. Toutes ne conviennent pas non plus aux classes d'utilité.

48voto

morechilli Points 4889

Un modèle courant d'écriture de tests unitaires consiste à ne tester que les méthodes publiques.

Si vous constatez que vous avez beaucoup de méthodes privées que vous souhaitez tester, c'est normalement le signe que vous devez remanier votre code.

Ce serait une erreur de rendre ces méthodes publiques dans la classe où elles se trouvent actuellement. Cela romprait le contrat que vous voulez que cette classe ait.

Il peut être correct de les déplacer vers une classe d'aide et de les rendre publiques à cet endroit. Cette classe peut ne pas être exposée par votre API.

De cette façon, le code de test n'est jamais mélangé à votre code public.

Un problème similaire est de tester des classes privées, c'est-à-dire des classes que vous n'exportez pas depuis votre assembly. Dans ce cas, vous pouvez explicitement faire de votre assembly de code de test un ami de l'assembly de code de production en utilisant l'attribut InternalsVisibleTo.

1 votes

+ 1 yep ! Je viens d'arriver à cette conclusion en écrivant mes tests, bon conseil !

1 votes

Déplacer les méthodes privées que vous souhaitez tester mais ne pas exposer aux utilisateurs de l'API vers une classe différente qui n'est pas exposée et les rendre publiques à cet endroit est exactement ce que je recherchais.

0 votes

Merci @morechilli. Je n'avais jamais vu InternalsVisibleTo avant. Cela a rendu les tests beaucoup plus faciles.

21voto

Dafydd Giddins Points 1055

Il est possible de tester des méthodes privées en déclarant votre assemblage de test comme un assemblage ami de l'assemblage cible que vous testez. Voir le lien ci-dessous pour plus de détails :

http://msdn.microsoft.com/en-us/library/0tke9fxk.aspx

Cela peut être utile car cela permet de séparer le code de test du code de production. Je n'ai jamais utilisé cette méthode moi-même car je n'en ai jamais trouvé le besoin. Je suppose que vous pourriez l'utiliser pour essayer de tester des cas extrêmes que vous ne pouvez tout simplement pas reproduire dans votre environnement de test pour voir comment votre code le gère.

Comme cela a été dit, vous ne devriez pas avoir besoin de tester les méthodes privées. Il est plus que probable que vous souhaitiez refactoriser votre code en blocs plus petits. Une astuce qui pourrait vous aider à refactoriser est d'essayer de penser au domaine auquel votre système se rapporte et de penser aux objets "réels" qui habitent ce domaine. Vos objets/classes dans votre système doivent se rapporter directement à un objet réel, ce qui vous permettra d'isoler le comportement exact que l'objet doit contenir et de limiter les responsabilités de l'objet. Cela signifie que vous refactorez de manière logique plutôt que de simplement rendre possible le test d'une méthode particulière ; vous serez en mesure de tester le comportement des objets.

Si vous ressentez toujours le besoin de tester l'interne, vous pouvez également envisager de recourir à la simulation (mocking) dans vos tests, car il est probable que vous souhaitiez vous concentrer sur un seul morceau de code. Le mocking consiste à injecter les dépendances d'un objet dans celui-ci, mais les objets injectés ne sont pas les objets "réels" ou de production. Ce sont des objets factices avec un comportement codé en dur pour faciliter l'isolation des erreurs de comportement. Rhino.Mocks est un cadre de simulation gratuit et populaire qui écrira essentiellement les objets pour vous. TypeMock.NET (un produit commercial avec une édition communautaire disponible) est un cadre plus puissant qui peut simuler des objets CLR. Très utile pour simuler les classes SqlConnection/SqlCommand et Datatable, par exemple lors du test d'une application de base de données.

Nous espérons que cette réponse vous donnera un peu plus d'informations pour vous informer sur les tests unitaires en général et vous aider à obtenir de meilleurs résultats avec les tests unitaires.

13 votes

Il est possible de tester interne et non privées (avec NUnit).

4voto

Maxime Rouiller Points 5987

L'objectif principal des tests unitaires est de tester les méthodes publiques d'une classe. Ces méthodes publiques vont utiliser les méthodes privées. Les tests unitaires testent le comportement de ce qui est publiquement disponible.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X