共享自己的一个单元测试帮助类-UnitTestHelper

    单元测试在开发过程中的重要性不言而喻,很多时候仅使用Assert.*来进行断言检查仍然是不够的,Assert只知道对与错,适合于自动化测试,对程序的调试却帮助有限。试着想像这么一种情形:我们写了一个数据库访问类MyInfoDA,针对数据库进行CURD操作,其中有一个方法GetInfoById返回一个实体MyInfo的实例,我们可以用Assert.IsNotNull简单的检查是否有实例返回,却无法了解MyInfo的内部结构是怎样的。最简单的办法或许就是Console.WriteLine挨个将MyInfo的内容Print出来,或者就是直接在IDE调试器里面查看对象内容。而我很厌烦这么做,于是就写了这么一个帮助类,感觉还是蛮有用途的,因此公布出来,供大家参考。

  1using System.Diagnostics;
  2using System.Reflection;
  3using System.Text;
  4
  5namespace NHTSS.UnitTest
  6{
  7    /// <summary>
  8    /// 单元测试的帮助类。
  9    /// </summary>

 10    public sealed class UnitTestHelper
 11    {
 12        private UnitTestHelper()
 13        {
 14        }

 15
 16        /// <summary>
 17        /// 格式化输出一个对象的所有属性值。
 18        /// </summary>
 19        /// <param name="obj">格式化输出一个对象的所有属性值。</param>
 20        /// <returns>对象名属性值。</returns>

 21        public static string ObjectProperty2String(object obj)
 22        {
 23            return ObjectProperty2String(obj, new Type[] {});
 24        }

 25
 26        /// <summary>
 27        /// 格式化输出一个对象的所有属性值。
 28        /// </summary>
 29        /// <param name="obj">格式化输出一个对象的所有属性值。</param>
 30        /// <param name="exposeItems">特别需要暴露的类型。</param>
 31        /// <returns>对象名属性值。</returns>
 32        /// <remarks>没有跨层次概念,如果需要,应将链结构的类型都放置在exposeItems中。</remarks>

 33        public static string ObjectProperty2String(object obj, Type[] exposeItems)
 34        {
 35            int tmp = 0;
 36            return ObjectProperty2String(obj, exposeItems, ref tmp);
 37        }

 38
 39        private static string ObjectProperty2String(object obj, Type[] exposeItems, ref int layer)
 40        {
 41            layer++;
 42
 43            StringBuilder sb = new StringBuilder();
 44            if (obj != null)
 45            {
 46                Type objType = obj.GetType();
 47                sb.Append(objType.Name).Append(" ->");
 48                MemberInfo[] members = objType.GetMembers();
 49                if ((objType.IsValueType) && (!objType.IsPrimitive))
 50                {
 51                    // value type use Field.
 52                    foreach (MemberInfo m in members)
 53                    {
 54                        if (m.MemberType == MemberTypes.Field)
 55                        {
 56                            InvokePropertyOrField(sb, obj, objType, exposeItems, ref layer, m, BindingFlags.Default | BindingFlags.GetField);
 57                        }

 58                    }

 59                }

 60                else
 61                {
 62                    // reference type use Property.
 63                    foreach (MemberInfo m in members)
 64                    {
 65                        if (m.MemberType == MemberTypes.Property)
 66                        {
 67                            InvokePropertyOrField(sb, obj, objType, exposeItems, ref layer, m, BindingFlags.Default | BindingFlags.GetProperty);
 68                        }

 69                    }

 70                }

 71            }

 72
 73            layer--;
 74
 75            return sb.ToString();
 76        }

 77
 78        private static void InvokePropertyOrField(StringBuilder sb, object obj, Type objType, Type[] exposeItems, ref int layer, MemberInfo m, BindingFlags flags)
 79        {
 80            object val = null;
 81            if (m.Name == "Item")
 82            {
 83                try
 84                {
 85                    if (m.DeclaringType.Equals(m.ReflectedType))
 86                    {
 87                        // assume it is a index indicator [Array].
 88                        int count = (int) objType.InvokeMember("Count", BindingFlags.Default | BindingFlags.GetProperty, null, obj, new object[] {});
 89                        for (int k = 0; k < count; k++)
 90                        {
 91                            val = objType.InvokeMember("get_Item", BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, new object[] {k});
 92                            sb.Append("\n").Append("\t".PadRight(layer, '\t')).Append(m.Name).Append("[").Append(k).Append("]=<").Append(val).Append(">; ");
 93                            ExposeDetail(sb, val, exposeItems, ref layer);
 94                        }

 95                    }

 96                }

 97                catch (Exception ex)
 98                {
 99                    // use compiler swtich /d:TRACE
100                    //Trace.WriteLine(ex.Message);
101                    Console.WriteLine("<<ERROR>> " + ex.Message);
102                }

103            }

104            else
105            {
106                val = objType.InvokeMember(m.Name, flags, null, obj, new object[] {});
107                sb.Append("\n").Append("\t".PadRight(layer, '\t')).Append(m.Name).Append("=<").Append(val).Append(">; ");
108                ExposeDetail(sb, val, exposeItems, ref layer);
109            }

110        }

111
112        // recursion function.
113        private static void ExposeDetail(StringBuilder sb, object val, Type[] exposeItems, ref int layer)
114        {
115            if (sb == null || val == null || exposeItems == null)
116            {
117                return;
118            }

119            if (!ContainType(val.GetType(), exposeItems))
120            {
121                return;
122            }

123            string str = ObjectProperty2String(val, exposeItems, ref layer);
124            sb.Append("\r\n").Append("\t".PadRight(layer, '\t')).Append(str);
125        }

126
127        // estimate Type.
128        private static bool ContainType(Type valType, Type[] exposeItems)
129        {
130            if (exposeItems == null)
131            {
132                return false;
133            }

134            int count = exposeItems.Length;
135            for (int k = 0; k < count; k++)
136            {
137                if (valType == exposeItems[k])
138                {
139                    return true;
140                }

141            }

142            return false;
143        }

144    }

145}

146

    说明一下,其实思路很简单的,就是通过GetType()获得该对象的元数据,然后通过反射输出该对象的值域(Field和Property)。对集合对象,或者特别想要了解的属性或字段通过递归方式处理。
   UnitTestHelper 提供了两个静态重载方法ObjectProperty2String,也比较好理解。下面给出两个简单的例子:

 1/// <summary>
 2/// 测试简单的数据实体。
 3/// </summary>

 4[TestFixture]
 5public class TestBaseInfo
 6{
 7  [Test]
 8  public void TestBuildInfoMock()
 9  {
10    //InfoMock 是个简单的测试实体对象。
11    InfoMock mock = new InfoMock();
12    mock.Name = "Mock";
13    mock.Address = "Paradise";
14    mock.Age = "No.1";
15    mock.Company = "Chiron";
16    mock.Credit = "IBM + Google + Microsoft";
17    mock.InfoId = 0;
18    Console.Out.WriteLine(UnitTestHelper.ObjectProperty2String(mock));
19  }

20}

21
22/// <summary>
23/// 测试ParametersDA。
24/// </summary>

25[TestFixture]
26public class TestParametersDA
27{
28  [Test]
29  public void TestGetAllParameters()
30  {
31    // ParameterCollection是一个数据实体集合。
32    ParameterCollection coll = ParametersDA.Instance.GetAllParams();
33    Console.Out.WriteLine(UnitTestHelper.ObjectProperty2String(coll, new Type[]typeof(ParameterInfo) }));
34  }

35}

P.S. 这是本人在博客园的第一篇正式技术文章,严格说来也没啥技术,只是一点实用技巧,以前很少动手写东西,肤浅不足之处,欢迎拍砖。
posted @ 2006-03-20 23:52  bengxia  阅读(739)  评论(5编辑  收藏  举报
无觅相关文章插件,快速提升流量