我正在尝试在单元测试项目中测试私有方法。到目前为止,它变得很棒,但是当我必须使用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'out变量在方法内设置,然后返回它的true或false值。但是当测试返回到它自己的代码时,'ssi'变量仍为null。所以问题是我在“GotSSI”方法中创建的对象没有从PrivateObject调用方法中解析出来。
任何人都知道我错过了什么?
更新(拉法尔解决方案)
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);
解决方案非常简单,就像魅力一样。
答案 0 :(得分:8)
虽然接受的最终解决方案有效,但有一种更简单的方法。如果你按照拉法尔接受的答案中给出的链接,你会发现一个类似的问题,有两个答案。第二个答案(最“有用”)是两者中较简单的。
以下是该答案的修改版本,专门针对测试场景:
//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;
答案 1 :(得分:3)
如果要获取out值,则使用out参数调用方法是错误的。有关如何使用反射调用它,请参阅this。
答案 2 :(得分:0)
你需要问自己是否真的需要测试私有方法?我个人不测试私人方法,但这完全取决于个人意见(并且它可以变得非常激烈)。有很多原因/文章/意见。 A good SO thread can be found here
接受答案的摘录是“私有方法是应该隐藏给类用户的实现细节。测试私有方法会破坏封装...... ”
我不测试私有方法的原因是因为它们比公共接口更容易改变。如果你涵盖了所有的私有方法,它会使重构变得更复杂(再次,只是我的意见)。如果您更改了私有方法并且公共接口断开,您将知道您的单元测试失败,然后您可以向下钻取。
这只是我的意见,我知道很多人不同意所以只是把它放在那里!
答案 3 :(得分:0)
我可以使用 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];