【问题标题】:PrivateObject wont return out parameterPrivateObject 不会返回参数
【发布时间】:2013-01-08 11:01:00
【问题描述】:

我正在尝试在单元测试项目中测试私有方法。到目前为止,一切都很好,但是当我必须测试一个带有 out 参数的方法时,我遇到了一个问题。该方法的签名是:

private bool GotSSI(out SSI ssi, RSI rsi)
{
        ~code omitted~
}

而单元测试(不工作的部分)看起来像这样:

SSI ssi = null;
object[] p = new object[]{ssi,rsi};
Type[] t = new Type[] { typeof(SSI).MakeByRefType(), typeof(RSI) };
actual = (bool) privateTarget.Invoke("GotSSI",t,p);

GotSSI 方法有效。我已经在单元测试中以调试模式对其进行了测试,我可以看到在方法内部设置了“ssi”输出变量,然后返回它的真值或假值。但是当测试返回到它自己的代码时,“ssi”变量仍然为空。所以问题是我在“GotSSI”方法中创建的对象,并没有从 PrivateObject 调用方法中解析出来。

有人知道我错过了什么吗?

更新(Rafal 的解决方案)

Rafal 的解决方案完美运行,这就是我实施该解决方案的方式。

我创建了一个委托:

delegate bool GotSSIInternal(out SSI ssi, RSI rsi);

当我创建了我想测试的对象后,我构建了委托(目标是我正在测试的对象):

GotSSIInternal gotSSIInternal = (GotSSIInternal) Delegate.CreateDelegate(
            typeof (GotSSIInternal), 
            target,
            typeof(OfflineResolver).GetMethod("GotSSI", BindingFlags.NonPublic | BindingFlags.Instance));

之后调用委托就很简单了:

actual = gotSSIInternal.Invoke(out ssi, rsi);

解决方案非常简单,效果很好。

【问题讨论】:

  • 定义一个帮助类来为你制作这些可能是有意义的: internal static T MakeDelegate(string methodName, T target){ return (T)Delegate.CreateDelegate(typeof(T ), target, typeof(C).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance));} 但也许你已经想到了。 :-)
  • 并非如此。只有一个测试有这个问题,而且我面临时间压力。一个好主意。

标签: c# unit-testing


【解决方案1】:

虽然被接受的最终解决方案可行,但有一种更简单的方法可以做到这一点。如果您按照 Rafal 接受的答案中给出的链接,您会发现与此问题类似的问题,有两个答案。第二个答案(最“有用”的点)是两者中最简单的一个。

这是该答案的修改版本,专门针对测试场景:

//method to test is a private method of the class ClassTotest with signature 
//    TryGetCustomerData(List<Invoice> invoices, out CustomerData customerData) 
//set up
var objToTest = new ClassToTest();
var invoices = GetInvoices();
CustomerData customerData = null;

//set up the arguments
var args = new object[] { invoices, customerData };
//get the MethodInfo of the method you want to test
var method = typeof(ClassToTest).GetMethod("TryGetCustomerData",
    BindingFlags.NonPublic | BindingFlags.Instance);
//invoke it
var success = (bool)method.Invoke(objToTest, args);
//get back the customerData argument - the "out" parameter
var actual = args[1] as CustomerData;

【讨论】:

  • 我同意,这更简单。我试过了,它对我有用,谢谢!
【解决方案2】:

如果您想获取 out 值,则使用 out 参数调用方法是错误的。请参阅this,了解如何通过反射调用它。

【讨论】:

    【解决方案3】:

    您需要问自己是否真的需要测试私有方法?我个人不测试私有方法,但这完全取决于个人意见(而且它可能会变得非常激烈)。有很多原因/文章/意见。 A good SO thread can be found here.

    已接受答案的摘录是“私有方法是一个实现细节,应该对类的用户隐藏。测试私有方法会破坏封装......

    我不测试我的私有方法的原因是因为它们比公共接口更容易改变。如果您涵盖所有私有方法,它会使重构更加复杂(再次,只是我的观点)。如果您更改私有方法并且公共接口中断,您将知道您的单元测试失败,然后您可以向下钻取。

    这只是我的意见,我知道很多人不同意,所以把它放在那里!

    【讨论】:

    • 我知道对单元测试私有方法的普遍共识,通常我不这样做。这里的问题是,我正在测试的这些方法从我可以使用的公共方法设置起来很复杂。使用私有测试更容易控制和理解。
    • 好奇谁/为什么投反对票?您有权投反对票,但评论为什么会有所帮助,否则这似乎毫无意义。
    【解决方案4】:

    我可以使用PrivateObject 来执行此操作,尽管它在技术上不是“返回值”,例如:

    object[] p = new object[] { null, rsi };
    Type[] t = new Type[] { typeof(SSI).MakeByRefType(), typeof(RSI) };
    actual = (bool)privateTarget.Invoke("GotSSI", t, p);
    SSI ssi = p[0];
    

    如果没有它可以确定方法重载,实际上可以省略类型数组:

    object[] p = new object[] { null, rsi };
    actual = (bool)privateTarget.Invoke("GotSSI", p);
    SSI ssi = p[0];
    

    【讨论】:

      【解决方案5】:

      只需这样做:https://instance-factory.com/?p=738

      ClassWithPrivateMethods instance = new ClassWithPrivateMethods();
      
      PrivateObject privateObject = new PrivateObject(instance);
      
      object[] args 
        = new object[] { "some value", null /* placeholder for out param */ };
      
      privateObject.Invoke("PrivateMethodHavingOutParameter", args);
      
      var outParameterValue = args[1];
      

      【讨论】:

        猜你喜欢
        • 2016-11-21
        • 2013-05-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-25
        相关资源
        最近更新 更多