Как я могу использовать PrivateObject для доступа к закрытым членам как моего класса, так и его родителя?

Я тестирую класс, который является частью иерархии. Я настраивал свои тестовые классы с тестируемым объектом и PrivateObject, чтобы разрешить доступ к этому объекту. Я получаю исключения, когда пытаюсь получить доступ к закрытым членам родительского класса.

Единственным обходным решением, которое я нашел до сих пор, является передача PrivateType, указывающая базовый класс на конструктор PrivateObject, но затем он не работает с частными членами подкласса.

Есть ли способ, которым я могу это сделать, возможно, используя параметр флаги привязки в методах Get * объекта Private?

Я попытался использовать автоматически созданные классы Accessor (щелкните правой кнопкой мыши в основном классе, Create Private Accessor). Тем не менее, это хуже: он показывает свойство, которое я могу прочитать, но он генерирует то же исключение, что и privateObject, и нет никаких других параметров, которые я могу использовать (флаги привязки или что-то еще), чтобы исправить исключение.

Вот мой примерный тестовый код. Я бы хотел, чтобы был какой-то способ построить и использовать PrivateObject для извлечения обоих полей.

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.
    }
}

Ответ 1

Я не нашел ответа, так что это то, что я делал. Я создал PrivateObjects для каждого уровня иерархии классов, и мне просто нужно быть осторожным при написании тестовых примеров, что я использую правильный.

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"));
    }
}

Ответ 2

Вероятно, это не тот ответ, который вы хотите... но вы не должны тестировать оба класса в одном методе в первую очередь. Вы должны всегда тестировать только один класс за раз. Если вы чувствуете необходимость сделать это, я бы предположил, что ваш код нуждается в рефакторинге. Но, поскольку я не знаю вашу проблему с реальным кодом, я не могу сказать точно

Ответ 3

PrivateObject немного находится на "тупой" стороне, когда дело касается обработки наследования. К сожалению, его методы не являются виртуальными, поэтому нет простого способа изменить это поведение. У вас по существу есть два варианта: либо жить с ограничениями, либо создавать свой собственный помощник-помощник, который может обрабатывать унаследованные элементы прозрачно.

Ответ 4

// 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");

Ответ 5

Как писал Андре Пена. Почему вы хотели бы протестировать элементы private базового класса через подкласс. У вас также не будет доступа к этим членам в обычном коде вашего подкласса. Вы должны сделать вкладкам Свойства protected доступ к ним из подкласса.

Затем вы также можете протестировать этих членов с PrivateObject.

Ответ 6

Я хотел сделать то же самое, и я сделал эти методы расширения. Теперь он работает хорошо. Моя первоначальная идея - с вашего поста. Спасибо!

https://github.com/cactuaroid/PrivateObjectExtensions

В основном это поиск члена-владельца Type.GetFields() и Type.GetProperties() рекурсивно, а затем создание PrivateObject (или PrivateType) как правильный тип для доступа к члену.