web-dev-qa-db-fra.com

Comment utiliser PrivateObject pour accéder aux membres privés de ma classe et de son parent?

Je teste une classe qui fait partie d'une hiérarchie. J'ai configuré mes classes de test avec l'objet à tester et une variable PrivateObject pour autoriser l'accès à cet objet. Je reçois des exceptions lorsque je tente d'accéder à des membres privés de la classe parent. 

La seule solution que j'ai trouvée jusqu'à présent consiste à passer une variable PrivateType spécifiant la classe de base au constructeur PrivateObject, mais cela ne fonctionne pas pour les membres privés de la sous-classe. 

Y a-t-il un moyen de le faire, peut-être en utilisant le paramètre flags de liaison dans les méthodes Get * d'objet privé?

J'ai essayé d'utiliser les classes d'accesseur générées automatiquement (cliquez avec le bouton droit de la souris sur la classe principale, Create Private Accessor). Cependant, c'est pire: il montre une propriété que je peux lire, mais il lève la même exception que PrivateObject, et je ne peux utiliser aucune autre option (indicateurs de liaison ou autre) pour corriger l'exception.

Voici mon exemple de code de test. J'aimerais qu'il y ait un moyen de construire et d'utiliser PrivateObject pour récupérer les deux champs. 

public class BaseClass
{
    private int one = 1;
}

public class SubClass : BaseClass
{
    private int two = 2;
}

[TestClass]
public class UnitTest1
{
    BindingFlags flags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;

    [TestMethod]
    public void TestMethod1()
    {
        SubClass test = new SubClass();
        PrivateObject priv = new PrivateObject(test);

        Assert.AreNotEqual<int>(0, (int)priv.GetFieldOrProperty("one", flags)); // System.MissingMethodException: Method 'PrivateObjectTester.SubClass.one' not found.
        Assert.AreNotEqual<int>(0, (int)priv.GetFieldOrProperty("two", flags));
    }

    [TestMethod]
    public void TestMethod2()
    {
        SubClass test = new SubClass();
        PrivateObject priv = new PrivateObject(test, new PrivateType(typeof(BaseClass)));

        Assert.AreNotEqual<int>(0, (int)priv.GetFieldOrProperty("one", flags));
        Assert.AreNotEqual<int>(0, (int)priv.GetFieldOrProperty("two", flags)); // System.MissingMethodException: Method 'PrivateObjectTester.BaseClass.two' not found.
    }
}
23
David Yaw

Je n'ai pas trouvé la réponse, alors c'est ce que j'ai fini par faire. J'ai créé PrivateObjects pour chaque niveau de la hiérarchie de la classe et je dois juste faire attention lorsque j'écris des scénarios de test que j'utilise le bon.

public class BaseClass
{
    private int one = 1;
}

public class SubClass : BaseClass
{
    private int two = 2;
}

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod()
    {
        SubClass test = new SubClass();
        PrivateObject privSub = new PrivateObject(test, new PrivateType(typeof(SubClass)));
        PrivateObject privBase = new PrivateObject(test, new PrivateType(typeof(BaseClass)));

        Assert.AreNotEqual<int>(0, (int)privBase.GetFieldOrProperty("one"));
        Assert.AreNotEqual<int>(0, (int)privSub.GetFieldOrProperty("two"));
    }
}
31
David Yaw

Ce n'est probablement pas la réponse que vous voulez ... mais vous ne devriez pas tester les deux classes avec une seule méthode. Vous ne devriez jamais tester qu'une classe à la fois. Si vous ressentez le besoin de le faire, alors je suppose que votre code doit être refactorisé. Mais comme je ne connais pas votre problème de code réel, je ne peux pas le dire avec certitude

9
AYoung
// create an instance of class SearchPlanogramsBuilder:
SearchPlanogramsBuilder searchPlanogramBuilder = new SearchPlanogramsBuilder(); 

// executing the method BuildSearchParameters(return type is void) with input searchPlanoGramsFilters:
searchPlanogramBuilder.BuildSearchParameters(searchPlanoGramsFilters);

// create privateobject and pass instance created for the class:
PrivateObject helperobject1 = new PrivateObject(searchPlanogramBuilder);

// type cast exactly as parameter(which is private variable) in the method:
Collection<string> parameter = (Collection<string>)helperobject1.GetFieldOrProperty("parameters");
1
lokesh

Je voulais faire la même chose et j'ai créé cette méthode d'extension. Maintenant ça marche bien. Mon idée de départ provient de votre post. Je vous remercie!

https://github.com/cactuaroid/PrivateObjectExtensions

Cela consiste essentiellement à rechercher le propriétaire du membre par Type.GetFields() et Type.GetProperties() de manière récursive, puis à créer PrivateObject (ou PrivateType) comme type correct pour accéder au membre.

1
cactuaroid

Comme l'a écrit André Pena. Pourquoi voudriez-vous tester les membres private de la classe Baseclass via la sous-classe. Vous n’auriez pas non plus accès à ces membres dans le code normal de votre sous-classe. Vous devez rendre les propriétés Properties protected pour y avoir accès à partir de la sous-classe.

Ensuite, vous pouvez également tester ces membres avec PrivateObject.

1
MrCube

PrivateObject est un peu "bête" en matière de gestion de l'héritage. Malheureusement, ses méthodes ne sont pas virtuelles. Il n’existe donc pas de moyen simple de changer ce comportement. Vous avez essentiellement deux options: vivre avec les limitations ou créer votre propre assistant d'accesseur privé capable de gérer les membres hérités de manière transparente.

1
Nicole Calinoiu

Utilisez PrivateType pour spécifier le type souhaité et utilisez un autre constructeur de PrivateObject:

var test = new SubClass();
var privateType = new PrivateType(typeof(BaseClass));
var privateObject = new PrivateObject(test, privateType);
// privateObject.GetFieldOrProperty("one", flags)
0
DonnyTian