How can I use PrivateObject to access private members of both my class and its parent?

44,954

Solution 1

I didn't find the answer, so this is what I ended up doing. I created PrivateObjects for each level of the class's hierarchy, and I just need to be careful when writing test cases that I use the proper one.

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

Solution 2

This likely isn't the answer you want...but you shouldn't be testing both classes in one method in the first place. You should only ever be testing one class at a time. If you feel the need to do this, then I'd guess that your code needs refactoring. But as I don't know your real-life code problem, I can't say for sure

Solution 3

I wanted to do same thing and I made this extension methods. Now it works well. My initial idea is from your post. Thank you!

https://github.com/cactuaroid/PrivateObjectExtensions

What it's doing basically is finding member owner by Type.GetFields() and Type.GetProperties() recursively, and then create PrivateObject (or PrivateType) as correct type to access the member.

Solution 4

Use PrivateType to specify the desired type and use a different constructor of PrivateObject:

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

Solution 5

// 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");
Share:
44,954
David Yaw
Author by

David Yaw

Updated on February 10, 2023

Comments

  • David Yaw
    David Yaw about 1 year

    I'm testing a class that is part of a hierarchy. I've been setting up my test classes with the object under test, and a PrivateObject to allow access to that object. I'm getting exceptions when I attempt to access private members of the parent class.

    The only workaround I've found so far is to pass a PrivateType specifying the base class to the PrivateObject constructor, but then it doesn't work on private members of the subclass.

    Is there some way I can do this, perhaps by using the binding flags parameter on the Get* methods of Private object?

    I did try using the automatically-generated Accessor classes (right-click in the main class, Create Private Accessor). However, that's worse: It shows a property I can read, but it throws the same exception as PrivateObject does, and there's no other options I can use (binding flags or whatnot) to fix the exception.

    Here's my sample test code. I'd like there to be some way to construct and use the PrivateObject to retrieve both fields.

    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.
        }
    }
    
  • David Yaw
    David Yaw about 13 years
    I'm not really testing two classes, I'm testing a class, along with its base class. In my case, I'm testing some cleanup behavior: When the object cleans itself up, I'm checking that all of the cleanup tasks have happened properly, and some of those are in the base, and some in the subclass. You are correct, ideally, I should be testing base class cleanup by itself, but part of this test is testing abnormal cleanup that only can happen with this particular subclass.
  • kodjeff1
    kodjeff1 almost 10 years
    Consider that you are writing tests for public functionality found on an abstract class. In order to test the class, you create a derived mock class that you can instantiate. In that case, you may still want to muck around with the values of private variables (say a property which is bound to by the UI, which has no public/protected setter). I don't agree that changing the members to accommodate ease of testing is the answer.
  • Mario Villanueva
    Mario Villanueva about 2 years
    Is important to said, nuget package is MSTest.TestFramework