您的位置:首页 - 教程 - Visual Studio - 正文
从vs2010的UnitTestFramework类库提取私有方法反射调用的方法

背景

年龄大点的程序员都知道在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


评论: