Как я могу использовать PrivateObject для доступа к закрытым членам моего класса и его родителя? - PullRequest
23 голосов
/ 22 марта 2011

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

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

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

Я попытался использовать автоматически сгенерированные классы 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.
    }
}

Ответы [ 7 ]

31 голосов
/ 30 марта 2011

Я не нашел ответа, так что это то, что я в итоге сделал. Я создал 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"));
    }
}
9 голосов
/ 15 апреля 2011

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

1 голос
/ 09 октября 2016

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

https://github.com/cactuaroid/PrivateObjectExtensions

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

1 голос
/ 20 ноября 2012

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

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

1 голос
/ 10 августа 2012
// 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 голос
/ 15 апреля 2011

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

0 голосов
/ 14 мая 2018

Используйте PrivateType для указания нужного типа и используйте другой конструктор PrivateObject:

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