如何使用PrivateObject访问我的类及其父类的私有成员?

32

我正在测试一个继承体系中的类。我一直在为我的测试类设置被测对象和一个PrivateObject,以允许访问该对象。当我尝试访问父类的私有成员时,我收到了异常。

到目前为止,我唯一找到的解决方法是通过向PrivateObject构造函数传递指定基类的PrivateType,但这样做无法使用子类的私有成员。

是否有一些方法可以做到这一点,也许通过在Private object的Get*方法上使用绑定标志参数?

我尝试过使用自动生成的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
只是顺便提一下,您可以使用PrivateObject访问受保护的成员。 - Andre Pena
7个回答

38

我没有找到答案,所以我最终做的是这样的。我为每个级别的类层次结构创建了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"));
    }
}

10

这可能不是你想要的答案……但是你一开始就不应该在一个方法中测试两个类。你只应该每次测试一个类。如果你觉得需要这样做,那么我猜你的代码需要重构。但是由于我不了解你真正的代码问题,所以不能确定。


2
我并不是在测试两个类,而是在测试一个类以及它的基类。在我的情况下,我正在测试一些清理行为:当对象进行自我清理时,我会检查所有的清理任务是否已经正确完成,其中一些在基类中,一些在子类中。你是正确的,理想情况下,我应该单独测试基类的清理,但这个测试的一部分是测试只有在这个特定的子类中才可能发生的异常清理。 - David Yaw

2

使用PrivateType指定所需的类型,并使用PrivateObject的不同构造函数:

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

重要的是,NuGet包是MSTest.TestFramework。 - Mario Villanueva

2
我想做同样的事情,于是我编写了这个扩展方法。现在它运行得很好。我的初始想法来自于你的帖子。谢谢!基本上它所做的是通过Type.GetFields()和Type.GetProperties()递归地找到成员所有者,然后创建正确类型的PrivateObject(或PrivateType)以访问成员。请参见https://github.com/cactuaroid/PrivateObjectExtensions

1
// 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

在处理继承方面,PrivateObject有点“愚蠢”。不幸的是,它的方法不是虚拟的,因此没有简单的方法可以更改这种行为。你基本上有两个选择:要么接受这些限制,要么创建自己的私有访问器助手,可以透明地处理继承成员。


1
正如André Pena所写。为什么您想通过子类测试Baseclass的private成员。在子类的正常代码中,您也无法访问这些成员。您必须将属性成员设置为protected,才能从子类中访问它们。
然后,您还可以使用PrivateObject测试这些成员。

考虑到你正在为一个抽象类中的公共功能编写测试。为了测试该类,你创建了一个派生的模拟类来进行实例化。在这种情况下,您可能仍然希望操作私有变量的值(例如一个绑定到UI的属性,其中没有公共/受保护的setter)。我不同意改变成员以适应测试的便利性是答案。 - kodjeff1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接