我正在构建一个类库,它将有一些公共和私有方法。我希望能够对私有方法进行单元测试(主要是在开发过程中,但也可以用于将来的重构)。
正确的做法是什么?
我正在构建一个类库,它将有一些公共和私有方法。我希望能够对私有方法进行单元测试(主要是在开发过程中,但也可以用于将来的重构)。
正确的做法是什么?
当前回答
对于任何想要运行私有方法的人来说。这适用于任何单元测试框架,只使用旧的Reflection。
public class ReflectionTools
{
// If the class is non-static
public static Object InvokePrivate(Object objectUnderTest, string method, params object[] args)
{
Type t = objectUnderTest.GetType();
return t.InvokeMember(method,
BindingFlags.InvokeMethod |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.Static,
null,
objectUnderTest,
args);
}
// if the class is static
public static Object InvokePrivate(Type typeOfObjectUnderTest, string method, params object[] args)
{
MemberInfo[] members = typeOfObjectUnderTest.GetMembers(BindingFlags.NonPublic | BindingFlags.Static);
foreach(var member in members)
{
if (member.Name == method)
{
return typeOfObjectUnderTest.InvokeMember(method, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, null, typeOfObjectUnderTest, args);
}
}
return null;
}
}
然后在你的实际测试中,你可以这样做:
Assert.AreEqual(
ReflectionTools.InvokePrivate(
typeof(StaticClassOfMethod),
"PrivateMethod"),
"Expected Result");
Assert.AreEqual(
ReflectionTools.InvokePrivate(
new ClassOfMethod(),
"PrivateMethod"),
"Expected Result");
其他回答
首先,您不应该测试代码的私有方法。你应该测试“公共接口”或API,即类的公共内容。API是所有公开给外部调用者的公共方法。
原因是一旦您开始测试类的私有方法和内部结构,您就将类的实现(私有的东西)耦合到您的测试中。这意味着当您决定更改实现细节时,您也必须更改您的测试。
出于这个原因,你应该避免使用internalsvisibletoattribute。
以下是Ian Cooper关于这个主题的演讲:Ian Cooper: TDD,哪里出了问题
在调试模式下构建时,你也可以将其声明为public或internal(使用InternalsVisibleToAttribute):
/// <summary>
/// This Method is private.
/// </summary>
#if DEBUG
public
#else
private
#endif
static string MyPrivateMethod()
{
return "false";
}
它使代码膨胀,但在发布版本中它将是私有的。
MbUnit有一个很好的包装,叫做Reflector。
Reflector dogReflector = new Reflector(new Dog());
dogReflector.Invoke("DreamAbout", DogDream.Food);
您还可以从属性中设置和获取值
dogReflector.GetProperty("Age");
关于“私人测试”,我同意…在完美的世界里。做私有单元测试是没有意义的。但在现实世界中,您可能最终希望编写私有测试,而不是重构代码。
您可以在Visual studio 2008中为私有方法生成测试方法。当您为私有方法创建单元测试时,test References文件夹将添加到测试项目中,访问器将添加到该文件夹中。存取器也在单元测试方法的逻辑中被引用。这个访问器允许单元测试调用正在测试的代码中的私有方法。 详情请看
http://msdn.microsoft.com/en-us/library/bb385974.aspx
我认为应该问的一个更基本的问题是,为什么要首先测试私有方法。这是一种代码气味,你试图通过类的公共接口测试私有方法,而该方法是私有的,因为它是一个实现细节。人们应该只关心公共接口的行为,而不是它在背后是如何实现的。
如果我想测试私有方法的行为,通过使用公共重构,我可以将其代码提取到另一个类中(可能具有包级可见性,以确保它不是公共API的一部分)。然后我可以单独测试它的行为。
重构的产物意味着私有方法现在是一个独立的类,它已经成为原始类的合作者。通过它自己的单元测试,它的行为将被很好地理解。
然后,当我试图测试原始类时,我可以模拟它的行为,这样我就可以集中精力测试该类公共接口的行为,而不必测试公共接口的组合爆炸及其所有私有方法的行为。
我认为这类似于开车。当我开车时,我不会把引擎盖打开,这样我就能看到发动机在工作。我依靠汽车提供的接口,即转速计数器和速度计来知道发动机是否在工作。我依靠的是当我踩下油门踏板时,汽车实际上在移动。如果我想测试引擎,我可以单独检查它。: D
当然,如果您有遗留应用程序,直接测试私有方法可能是最后的手段,但我更希望对遗留代码进行重构,以实现更好的测试。迈克尔·费瑟就这个主题写了一本很棒的书。http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052