我正在构建一个类库,其中包含一些公共和私有方法。我希望能够单元测试私有方法(主要是在开发过程中,但也可能对未来的重构有用)。
正确的操作方式是什么?
我正在构建一个类库,其中包含一些公共和私有方法。我希望能够单元测试私有方法(主要是在开发过程中,但也可能对未来的重构有用)。
正确的操作方式是什么?
MbUnit推出了一个名为Reflector的不错的包装器。
Reflector dogReflector = new Reflector(new Dog());
dogReflector.Invoke("DreamAbout", DogDream.Food);
你也可以设置和获取属性的值。
dogReflector.GetProperty("Age");
关于“测试私有”,我同意在完美的世界中,没有必要进行私有单元测试。但在现实世界中,你可能会想编写私有测试而不是重构代码。
Reflector
已经被更强大的 Mirror
替代,出现在 Gallio/MbUnit v3.2 中。(http://www.gallio.org/wiki/doku.php?id=mbunit:mirror) - Yann Trevin我使用PrivateObject类。但是,如前所述,最好避免测试私有方法。
Class target = new Class();
PrivateObject obj = new PrivateObject(target);
var retVal = obj.Invoke("PrivateMethod");
Assert.AreEqual(retVal);
在调试模式下,您还可以将其声明为公共或内部(使用InternalsVisibleToAttribute):
/// <summary>
/// This Method is private.
/// </summary>
#if DEBUG
public
#else
private
#endif
static string MyPrivateMethod()
{
return "false";
}
这是一个例子,首先是方法签名:
private string[] SplitInternal()
{
return Regex.Matches(Format, @"([^/\[\]]|\[[^]]*\])+")
.Cast<Match>()
.Select(m => m.Value)
.Where(s => !string.IsNullOrEmpty(s))
.ToArray();
}
这是测试:
/// <summary>
///A test for SplitInternal
///</summary>
[TestMethod()]
[DeploymentItem("Git XmlLib vs2008.dll")]
public void SplitInternalTest()
{
string path = "pair[path/to/@Key={0}]/Items/Item[Name={1}]/Date";
object[] values = new object[] { 2, "Martin" };
XPathString xp = new XPathString(path, values);
PrivateObject param0 = new PrivateObject(xp);
XPathString_Accessor target = new XPathString_Accessor(param0);
string[] expected = new string[] {
"pair[path/to/@Key={0}]",
"Items",
"Item[Name={1}]",
"Date"
};
string[] actual;
actual = target.SplitInternal();
CollectionAssert.AreEqual(expected, actual);
}
1)如果你有一个遗留代码,那么测试私有方法的唯一方法是通过反射。
2)如果它是新代码,则有以下选项:
我更喜欢注释方法,最简单和最不复杂。唯一的问题是我们增加了可见性,我认为这不是一个大问题。 我们应该始终编写接口,所以如果我们有一个接口MyService和一个实现MyServiceImpl,那么我们可以有相应的测试类,即MyServiceTest(测试接口方法)和MyServiceImplTest(测试私有方法)。所有客户端都应该使用接口,因此即使私有方法的可见性已经增加,它也不应该真正的影响。
protected
,并编写一个测试夹具,该测试夹具继承你要测试的类。这样,你不会将你的方法改为public
,但可以进行测试。在C#中,您可以使用我提供的代码。虽然我认为只有在绝对需要的情况下才应该对私有方法进行单元测试。我遇到过一些情况,觉得这是必要的。以下是我在一个UnitTestBase
类中创建的一些C#方法,我从中继承我的UnitTest类(您也可以将其放在静态的“helper”类中)。希望对您有所帮助。
protected internal static TResult? InvokePrivateInstanceMethod<TType, TResult>(string methodName, object?[]? methodArguments = null, params object?[]? constructorArguments)
{
var classType = typeof(TType);
var instance = Activator.CreateInstance(classType, constructorArguments);
var privateMethodInfo = classType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(m => m.IsPrivate &&
m.Name.Equals(methodName, StringComparison.Ordinal) &&
m.ReturnType.Equals(typeof(TResult)));
if (privateMethodInfo is null)
{
throw new MissingMethodException(classType.FullName, methodName);
}
var methodResult = privateMethodInfo.Invoke(instance, methodArguments);
if (methodResult is not null)
{
return (TResult)methodResult;
}
return default;
}
protected internal static async Task<TResult?> InvokePrivateInstanceMethodAsync<TType, TResult>(string methodName, object?[]? methodArguments = null, params object?[]? constructorArguments)
{
var classType = typeof(TType);
var instance = Activator.CreateInstance(classType, constructorArguments);
var privateMethodInfo = classType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(m => m.IsPrivate &&
m.Name.Equals(methodName, StringComparison.Ordinal) &&
m.ReturnType.Equals(typeof(Task<TResult>)));
if (privateMethodInfo is null)
{
throw new MissingMethodException(classType.FullName, methodName);
}
var methodResult = privateMethodInfo.Invoke(instance, methodArguments);
if (methodResult is not null)
{
return await (Task<TResult>)methodResult;
}
return default;
}