背景
年龄大点的程序员都知道在vs2010中创建单元测试非常的简单,鼠标定位在方法名字,右键创建单元测试,就会创建一个测试方法,即使是在私有方法上也可以创建测试方法。
VS2010以后就没这么简单了,微软默认取消了这种快捷方式,安装 Unit Test Generator 插件也只能在公有方法上创建单元测试。为了方便的测试私有方法,我们需要一种反射调用私有成员的方法。这种现成的方法可以在网上找到不少,我这里是讲述如何从VS2010
的UnitTestFramework类库提取反射调用私有成员的方法。
VS2010私有方法单元测试分析
在vs2010里新建个测试类:
public class Class1 { private string GetName(string name, int age) { return name + age; } }
Class1
创建单元测试
/// <summary> ///GetName 的测试 ///</summary> [TestMethod()] [DeploymentItem("ClassLibrary1.dll")] public void GetNameTest() { Class1_Accessor target = new Class1_Accessor(); // TODO: 初始化为适当的值 string name = string.Empty; // TODO: 初始化为适当的值 int age = 0; // TODO: 初始化为适当的值 string expected = string.Empty; // TODO: 初始化为适当的值 string actual; actual = target.GetName(name, age); Assert.AreEqual(expected, actual); Assert.Inconclusive("验证此测试方法的正确性。"); }
Class1 Test
有这么一行 [DeploymentItem("ClassLibrary1.dll")],多了个 Class1_Accessor
使用ILSpy打开ClassLibrary1.dll看看
可以看出Class1_Accessor : BaseShadow ,实例化时调用了基类的构造方法,实例化一个privateObject并赋给了m_privateObject ,GetName方法就是调用了m_privateObject 的invoke获取返回值。
protected BaseShadow(PrivateObject privateTarget) { this.m_privateObject = privateTarget; }
沿着 BaseShadow ->PrivateObject->PrivateType->RuntimeTypeHelper->Helper的顺序把相关的代码都保存下来,文章最后提供下载。
其中用到FrameworkMessages 的都是错误信息相关的,用到了资源文件。这里没有照搬而是把用到的几个信息敲了一遍,如下:
class FrameworkMessages { public static string PrivateAccessorMemberNotFound(string name) { return string.Format("The member specified ({0}) could not be found.You might need to regenerate your private accesscor the member may be private and defined on a base class." + "If the latter is true,you need to pass the type that defines the member into PrivateObject‘s constructor", name); } public static string AccessStringInvalidSyntax { get { return "Access string has invalid syntax."; } } public static string PrivateAccessorConstructorNotFound { get { return "The constructor with the specified signature could not be found.You might need to regenerate your private accesscor the member may be private and defined on a base class." + "If the latter is true,you need to pass the type that defines the member into PrivateObject‘s constructor"; } } }
FrameworkMessages
用到的方法找齐了,写个测试用例,首先定义一个需要访问的类
public class DemoClass { public string Name { get; set; } public int Age { get; set; } public DemoClass():this("zeroes",100) { } public DemoClass(string name, int age) { this.Name = name; this.Age = age; } private string PrivateMethod(DateTime time) { return "姓名:{0} 年龄:{1} 时间:{2}".FormatWith(this.Name, this.Age, time.ToString("yyyy")); } }
DemoClass
然后封装一个私有反射的帮助类
/// <summary> /// 调用方法单元测试管理类 /// </summary> public static class PrivateUnitTestUtil { /// <summary> /// 调用私有方法 /// </summary> /// <param name="t">要访问的类类型</param> /// <param name="methodName">方法名字</param> /// <param name="paras">参数</param> /// <returns></returns> public static object InvokeMethod(Type t, string methodName, params object[] paras) { var privateObject = new PrivateObject(t); return privateObject.Invoke(methodName, paras); } /// <summary> /// 调用私有方法 /// </summary> /// <param name="instance">要访问的类实例</param> /// <param name="methodName">方法名字</param> /// <param name="paras">参数</param> /// <returns></returns> public static object InvokeMethod(object instance, string methodName, params object[] paras) { var privateObject = new PrivateObject(instance); return privateObject.Invoke(methodName, paras); } }
PrivateUnitTestUtil
在测试项目中调用
[TestClass] public class PrivateUnitTestUtilTests { [TestMethod] public void InvokeMethodTest() { var instance = new DemoClass(); instance.Name = "111111"; DateTime time = DateTime.Now; var ret = (string) PrivateUnitTestUtil.InvokeMethod(instance, "PrivateMethod", time); Assert.AreEqual(ret, "姓名:{0} 年龄:{1} 时间:{2}".FormatWith(instance.Name, instance.Age, time.Year)); } }
PrivateUnitTestUtilTests
更多的调用方式可以慢慢补充,相关文件下载地址:http://download.csdn.net/detail/zbl131/9493247