web-dev-qa-db-fra.com

Comment testez-vous des méthodes privées avec NUnit?

Je me demande comment utiliser NUnit correctement. J'ai d'abord créé un test-projet distinct qui utilise mon projet principal comme référence. Mais dans ce cas, je ne suis pas en mesure de tester des méthodes privées. Je suppose que je dois 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'envoyer du code avec des tests.)

Comment testez-vous des méthodes privées avec NUnit?

99
MrFox

Généralement, les tests unitaires concernent l'interface publique d'une classe, sur la théorie que l'implémentation est immatérielle, tant que les résultats sont corrects du point de vue du client.

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

73
harpo

Bien que je convienne que l'objectif des tests unitaires devrait être l'interface publique, vous obtenez une impression beaucoup plus granulaire de votre code si vous testez également des méthodes privées. Le cadre de test MS permet cela grâce à l'utilisation de PrivateObject et PrivateType, NUnit ne le fait pas. Ce que je fais à la place, c'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 n'avez 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 est juste pour les méthodes d'instance.

50
user1039513

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

Si vous constatez que vous souhaitez tester de nombreuses méthodes privées, cela signifie généralement que vous devez refactoriser votre code.

Il serait faux de rendre ces méthodes publiques sur la classe où elles vivent actuellement. Cela romprait le contrat que vous souhaitez que cette classe ait.

Il peut être correct de les déplacer dans une classe auxiliaire et de les rendre publics là-bas. Cette classe peut ne pas être exposée par votre API.

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

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

46
morechilli

Il est possible de tester des méthodes privées en déclarant votre assembly de test comme un ami Assembly de l'assembly 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 il sépare principalement votre code de test de votre 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 pouvez l'utiliser pour essayer de tester des cas de test extrêmes que vous ne pouvez tout simplement pas répliquer dans votre environnement de test pour voir comment votre code le gère.

Comme cela a été dit cependant, vous ne devriez vraiment pas avoir besoin de tester des méthodes privées. Vous souhaitez plus que probablement refaçonner votre code en blocs de construction plus petits. Une astuce qui pourrait vous aider lorsque vous venez de refactoriser est d'essayer de penser au domaine auquel votre système se rapporte et de penser aux "vrais" objets qui habitent ce domaine. Vos objets/classes dans votre système doivent se rapporter directement à un objet réel qui vous permettra d'isoler le comportement exact que l'objet doit contenir et également de limiter les responsabilités des objets. Cela signifie que vous refactorisez logiquement plutôt que simplement pour permettre de tester une méthode particulière; vous pourrez tester le comportement des objets.

Si vous ressentez toujours le besoin de tester en interne, vous pouvez également envisager de vous moquer de vos tests car vous êtes susceptible de vouloir vous concentrer sur un morceau de code. La moquerie est l'endroit où vous y injectez des dépendances d'objets, mais les objets injectés ne sont pas des objets "réels" ou de production. Ce sont des objets factices avec un comportement codé en dur pour faciliter l'isolement des erreurs de comportement. Rhino.Mocks est un framework de simulation gratuit populaire qui écrit essentiellement les objets pour vous. TypeMock.NET (un produit commercial avec une édition communautaire disponible) est un framework plus puissant qui peut se moquer des objets CLR. Très utile pour se moquer des classes SqlConnection/SqlCommand et Datatable par exemple lors du test d'une application de base de données.

J'espère 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 à partir des tests unitaires.

20
Dafydd Giddins

Je suis favorable à la possibilité de tester des méthodes privées. Lorsque xUnit a démarré, il était destiné à tester les fonctionnalités après l'écriture du code. Le test de l'interface est suffisant à cet effet.

Les tests unitaires ont évolué vers un développement piloté par les tests. Avoir la capacité de tester toutes les méthodes est utile pour cette application.

6
Mark Glass

L'objectif principal des tests unitaires est de tester les méthodes publiques d'une classe. Ces méthodes publiques utiliseront ces méthodes privées. Les tests unitaires testeront le comportement de ce qui est accessible au public.

4
Maxime Rouiller

Cette question en est à ses débuts, mais j'ai pensé partager ma façon de procéder.

Fondamentalement, j'ai toutes mes classes de tests unitaires dans l'assembly qu'ils testent dans un espace de noms "UnitTest" en dessous de la "valeur par défaut" pour cet assembly - chaque fichier de test est enveloppé dans un:

#if DEBUG

...test code...

#endif

bloc, et tout cela signifie que a) il n'est pas distribué dans une version et b) je peux utiliser les déclarations de niveau internal/Friend sans saut de cercle.

L'autre chose que cela offre, plus pertinente pour cette question, est l'utilisation de classes partial, qui peuvent être utilisées pour créer un proxy pour tester des méthodes privées, donc par exemple pour tester quelque chose comme une méthode privée qui renvoie un valeur entière:

public partial class TheClassBeingTested
{
    private int TheMethodToBeTested() { return -1; }
}

dans les classes principales de l'Assemblée, et la classe de test:

#if DEBUG

using NUnit.Framework;

public partial class TheClassBeingTested
{
    internal int NUnit_TheMethodToBeTested()
    {
        return TheMethodToBeTested();
    }
}

[TestFixture]
public class ClassTests
{
    [Test]
    public void TestMethod()
    {
        var tc = new TheClassBeingTested();
        Assert.That(tc.NUnit_TheMethodToBeTested(), Is.EqualTo(-1));
    }
}

#endif

Évidemment, vous devez vous assurer que vous n'utilisez pas cette méthode pendant le développement, bien qu'une version Release indiquera bientôt un appel par inadvertance si vous le faites.

4
Stuart Wood

Toutes mes excuses si cela ne répond pas à la question, mais des solutions telles que l'utilisation de la réflexion, les instructions #if #endif ou la visibilité des méthodes privées ne résolvent pas le problème. Il peut y avoir plusieurs raisons pour ne pas rendre les méthodes privées visibles ... et si c'est du code de production et que l'équipe écrit rétrospectivement des tests unitaires par exemple.

Pour le projet sur lequel je travaille, seul MSTest (malheureusement) semble avoir un moyen, à l'aide d'accesseurs, de tester des méthodes privées.

3
Ritesh

Vous ne testez pas les fonctions privées. Il existe des moyens d'utiliser la réflexion pour accéder aux méthodes et propriétés privées. Mais ce n'est pas vraiment facile et je décourage fortement cette pratique.

Vous ne devriez tout simplement pas tester tout ce qui n'est pas public.

Si vous avez des méthodes et des propriétés internes, vous devriez envisager de changer cela en public, ou d'envoyer vos tests avec l'application (quelque chose que je ne considère pas vraiment comme un problème).

Si votre client est capable d'exécuter une suite de tests et de voir que le code que vous avez fourni fonctionne réellement, je ne vois pas cela comme un problème (tant que vous ne donnez pas votre adresse IP par ce biais). Les éléments que j'inclus dans chaque version sont des rapports de test et des rapports de couverture de code.

2
Tigraine

En théorie des tests unitaires, seul le contrat doit être testé. c'est-à-dire uniquement les membres publics de la classe. Mais dans la pratique, le développeur souhaite généralement tester les membres internes. - et ce n'est pas mal. Oui, cela va à l'encontre de la théorie, mais dans la pratique, cela peut parfois être utile.

Donc, si vous voulez vraiment tester les membres internes, vous pouvez utiliser l'une de ces approches:

  1. Rendez votre membre public. Dans de nombreux livres, les auteurs suggèrent cette approche comme simple
  2. Vous pouvez rendre vos membres internes et ajouter InternalVisibleTo à assebly
  3. Vous pouvez protéger les membres de la classe et hériter votre classe de test de votre classe sous-test.

Exemple de code (pseudo code):

public class SomeClass
{
    protected int SomeMethod() {}
}
[TestFixture]
public class TestClass : SomeClass{

    protected void SomeMethod2() {}
    [Test]
    public void SomeMethodTest() { SomeMethod2(); }
}
1
burzhuy

Vous pouvez rendre vos méthodes protégées en interne, puis utiliser Assembly: InternalVisibleTo("NAMESPACE") pour votre espace de noms de test.

Par conséquent, NON! Vous ne pouvez pas accéder aux méthodes privées, mais cela est une solution de contournement.

1
Furgalicious

Je rendrais visible le paquet des méthodes privées. De cette façon, vous le gardez raisonnablement privé tout en étant en mesure de tester ces méthodes. Je ne suis pas d'accord avec les gens qui disent que les interfaces publiques sont les seules qui devraient être testées. Il y a souvent du code vraiment critique dans les méthodes privées qui ne peut pas être correctement testé en passant uniquement par les interfaces externes.

Donc, cela se résume vraiment à si vous vous souciez plus du code correct ou des informations cachées. Je dirais que la visibilité du package est un bon compromis car pour accéder à cette méthode, quelqu'un devrait placer sa classe dans votre package. Cela devrait vraiment leur faire réfléchir à deux fois pour savoir si c'est une chose vraiment intelligente à faire.

Je suis un Java guy btw, donc la visibilité du package peut être appelée quelque chose de complètement différent en C #. Il suffit de dire que c'est lorsque deux classes doivent être dans le même espace de noms pour accéder à ces méthodes .

0
Fylke