共享自己的一个单元测试帮助类-UnitTestHelper
单元测试在开发过程中的重要性不言而喻,很多时候仅使用Assert.*来进行断言检查仍然是不够的,Assert只知道对与错,适合于自动化测试,对程序的调试却帮助有限。试着想像这么一种情形:我们写了一个数据库访问类MyInfoDA,针对数据库进行CURD操作,其中有一个方法GetInfoById返回一个实体MyInfo的实例,我们可以用Assert.IsNotNull简单的检查是否有实例返回,却无法了解MyInfo的内部结构是怎样的。最简单的办法或许就是Console.WriteLine挨个将MyInfo的内容Print出来,或者就是直接在IDE调试器里面查看对象内容。而我很厌烦这么做,于是就写了这么一个帮助类,感觉还是蛮有用途的,因此公布出来,供大家参考。
1
using System.Diagnostics;
2
using System.Reflection;
3
using System.Text;
4![](/Images/OutliningIndicators/None.gif)
5
namespace NHTSS.UnitTest
6
{
7
/// <summary>
8
/// 单元测试的帮助类。
9
/// </summary>
10
public sealed class UnitTestHelper
11
{
12
private UnitTestHelper()
13
{
14
}
15![](/Images/OutliningIndicators/InBlock.gif)
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![](/Images/OutliningIndicators/InBlock.gif)
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![](/Images/OutliningIndicators/InBlock.gif)
39
private static string ObjectProperty2String(object obj, Type[] exposeItems, ref int layer)
40
{
41
layer++;
42![](/Images/OutliningIndicators/InBlock.gif)
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![](/Images/OutliningIndicators/InBlock.gif)
73
layer--;
74![](/Images/OutliningIndicators/InBlock.gif)
75
return sb.ToString();
76
}
77![](/Images/OutliningIndicators/InBlock.gif)
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![](/Images/OutliningIndicators/InBlock.gif)
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![](/Images/OutliningIndicators/InBlock.gif)
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![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
2
![](/Images/OutliningIndicators/None.gif)
3
![](/Images/OutliningIndicators/None.gif)
4
![](/Images/OutliningIndicators/None.gif)
5
![](/Images/OutliningIndicators/None.gif)
6
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
7
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
8
![](/Images/OutliningIndicators/InBlock.gif)
9
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
10
![](/Images/OutliningIndicators/InBlock.gif)
11
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
12
![](/Images/OutliningIndicators/InBlock.gif)
13
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
14
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
15
![](/Images/OutliningIndicators/InBlock.gif)
16
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
17
![](/Images/OutliningIndicators/InBlock.gif)
18
![](/Images/OutliningIndicators/InBlock.gif)
19
![](/Images/OutliningIndicators/InBlock.gif)
20
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
21
![](/Images/OutliningIndicators/InBlock.gif)
22
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
23
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
24
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
25
![](/Images/OutliningIndicators/InBlock.gif)
26
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
27
![](/Images/OutliningIndicators/InBlock.gif)
28
![](/Images/OutliningIndicators/InBlock.gif)
29
![](/Images/OutliningIndicators/InBlock.gif)
30
![](/Images/OutliningIndicators/InBlock.gif)
31
![](/Images/OutliningIndicators/InBlock.gif)
32
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
33
![](/Images/OutliningIndicators/InBlock.gif)
34
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
35
![](/Images/OutliningIndicators/InBlock.gif)
36
![](/Images/OutliningIndicators/InBlock.gif)
37
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
38
![](/Images/OutliningIndicators/InBlock.gif)
39
![](/Images/OutliningIndicators/InBlock.gif)
40
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
41
![](/Images/OutliningIndicators/InBlock.gif)
42
![](/Images/OutliningIndicators/InBlock.gif)
43
![](/Images/OutliningIndicators/InBlock.gif)
44
![](/Images/OutliningIndicators/InBlock.gif)
45
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
46
![](/Images/OutliningIndicators/InBlock.gif)
47
![](/Images/OutliningIndicators/InBlock.gif)
48
![](/Images/OutliningIndicators/InBlock.gif)
49
![](/Images/OutliningIndicators/InBlock.gif)
50
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
51
![](/Images/OutliningIndicators/InBlock.gif)
52
![](/Images/OutliningIndicators/InBlock.gif)
53
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
54
![](/Images/OutliningIndicators/InBlock.gif)
55
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
56
![](/Images/OutliningIndicators/InBlock.gif)
57
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
58
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
59
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
60
![](/Images/OutliningIndicators/InBlock.gif)
61
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
62
![](/Images/OutliningIndicators/InBlock.gif)
63
![](/Images/OutliningIndicators/InBlock.gif)
64
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
65
![](/Images/OutliningIndicators/InBlock.gif)
66
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
67
![](/Images/OutliningIndicators/InBlock.gif)
68
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
69
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
70
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
71
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
72
![](/Images/OutliningIndicators/InBlock.gif)
73
![](/Images/OutliningIndicators/InBlock.gif)
74
![](/Images/OutliningIndicators/InBlock.gif)
75
![](/Images/OutliningIndicators/InBlock.gif)
76
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
77
![](/Images/OutliningIndicators/InBlock.gif)
78
![](/Images/OutliningIndicators/InBlock.gif)
79
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
80
![](/Images/OutliningIndicators/InBlock.gif)
81
![](/Images/OutliningIndicators/InBlock.gif)
82
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
83
![](/Images/OutliningIndicators/InBlock.gif)
84
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
85
![](/Images/OutliningIndicators/InBlock.gif)
86
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
87
![](/Images/OutliningIndicators/InBlock.gif)
88
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
89
![](/Images/OutliningIndicators/InBlock.gif)
90
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
91
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
92
![](/Images/OutliningIndicators/InBlock.gif)
93
![](/Images/OutliningIndicators/InBlock.gif)
94
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
95
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
96
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
97
![](/Images/OutliningIndicators/InBlock.gif)
98
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
99
![](/Images/OutliningIndicators/InBlock.gif)
100
![](/Images/OutliningIndicators/InBlock.gif)
101
![](/Images/OutliningIndicators/InBlock.gif)
102
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
103
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
104
![](/Images/OutliningIndicators/InBlock.gif)
105
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
106
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
107
![](/Images/OutliningIndicators/InBlock.gif)
108
![](/Images/OutliningIndicators/InBlock.gif)
109
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
110
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
111
![](/Images/OutliningIndicators/InBlock.gif)
112
![](/Images/OutliningIndicators/InBlock.gif)
113
![](/Images/OutliningIndicators/InBlock.gif)
114
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
115
![](/Images/OutliningIndicators/InBlock.gif)
116
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
117
![](/Images/OutliningIndicators/InBlock.gif)
118
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
119
![](/Images/OutliningIndicators/InBlock.gif)
120
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
121
![](/Images/OutliningIndicators/InBlock.gif)
122
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
123
![](/Images/OutliningIndicators/InBlock.gif)
124
![](/Images/OutliningIndicators/InBlock.gif)
125
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
126
![](/Images/OutliningIndicators/InBlock.gif)
127
![](/Images/OutliningIndicators/InBlock.gif)
128
![](/Images/OutliningIndicators/InBlock.gif)
129
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
130
![](/Images/OutliningIndicators/InBlock.gif)
131
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
132
![](/Images/OutliningIndicators/InBlock.gif)
133
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
134
![](/Images/OutliningIndicators/InBlock.gif)
135
![](/Images/OutliningIndicators/InBlock.gif)
136
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
137
![](/Images/OutliningIndicators/InBlock.gif)
138
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
139
![](/Images/OutliningIndicators/InBlock.gif)
140
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
141
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
142
![](/Images/OutliningIndicators/InBlock.gif)
143
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
144
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
145
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
146
![](/Images/OutliningIndicators/None.gif)
说明一下,其实思路很简单的,就是通过GetType()获得该对象的元数据,然后通过反射输出该对象的值域(Field和Property)。对集合对象,或者特别想要了解的属性或字段通过递归方式处理。
UnitTestHelper 提供了两个静态重载方法ObjectProperty2String,也比较好理解。下面给出两个简单的例子:
1
/// <summary>
2
/// 测试简单的数据实体。
3
/// </summary>
4
[TestFixture]
5
public 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![](/Images/OutliningIndicators/None.gif)
22
/// <summary>
23
/// 测试ParametersDA。
24
/// </summary>
25
[TestFixture]
26
public 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
}
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
2
![](/Images/OutliningIndicators/InBlock.gif)
3
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
4
![](/Images/OutliningIndicators/None.gif)
5
![](/Images/OutliningIndicators/None.gif)
6
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
7
![](/Images/OutliningIndicators/InBlock.gif)
8
![](/Images/OutliningIndicators/InBlock.gif)
9
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
10
![](/Images/OutliningIndicators/InBlock.gif)
11
![](/Images/OutliningIndicators/InBlock.gif)
12
![](/Images/OutliningIndicators/InBlock.gif)
13
![](/Images/OutliningIndicators/InBlock.gif)
14
![](/Images/OutliningIndicators/InBlock.gif)
15
![](/Images/OutliningIndicators/InBlock.gif)
16
![](/Images/OutliningIndicators/InBlock.gif)
17
![](/Images/OutliningIndicators/InBlock.gif)
18
![](/Images/OutliningIndicators/InBlock.gif)
19
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
20
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
21
![](/Images/OutliningIndicators/None.gif)
22
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
23
![](/Images/OutliningIndicators/InBlock.gif)
24
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
25
![](/Images/OutliningIndicators/None.gif)
26
![](/Images/OutliningIndicators/None.gif)
27
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
28
![](/Images/OutliningIndicators/InBlock.gif)
29
![](/Images/OutliningIndicators/InBlock.gif)
30
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
31
![](/Images/OutliningIndicators/InBlock.gif)
32
![](/Images/OutliningIndicators/InBlock.gif)
33
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
34
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
35
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
P.S. 这是本人在博客园的第一篇正式技术文章,严格说来也没啥技术,只是一点实用技巧,以前很少动手写东西,肤浅不足之处,欢迎拍砖。