更新时间:2022-10-04 14:47:21
◆ 找准切入点:不论是开发一个新的系统还是复原系统,都必须先找准一个或多个切入点,从切入点经历”测试代码-功能代码-测试-重构“来逐渐完善整个系统,往往这个切入点就是功能点,就是这个系统具备哪些功能,然后根据这些功能写出测试用例。◆ 测试列表:大家都知道一个系统或者一个框架都是很庞大的,如果要引入测试驱动开发,首先我们必须要有一个测试列表,在任何阶段想添加功能需求问题时,把相 关功能点加到测试列表中,然后继续开发的工作。然后不断的完成对应的测试用例、功能代码、重构。这样可以避免疏漏的同时也能把控当前的进度。◆ 测试驱动:这个比较核心。完成某个功能,某个类,首先编写测试代码,考虑其如何使用、如何测试。然后在对其进行设计、编码。这里也强调先编写对功能代码的判断用的断言语句,然后编写相应的辅助语句。◆ 良好的代码设计及可测性:功能代码设计、开发时应该具有较强的可测试性。应该尽量保持良好的设计原则和代码规范,如尽量依赖于接口、尽量高内聚、低耦合等等。◆ 模块或功能隔离:不同代码的测试应该相互隔离。对一块代码的测试只考虑此代码的测试,不要考虑其实现细节,不然就会陷入一团乱麻之中,这个可以通过MOCK来实现,同时在开始的时候也要划分好边界。◆ 适当引入MOCK:在适当情况下引入MOCK来完成单元测试,这种情况尤其是在边际交互比较多的案例当中,对于交互比较多且复杂的多个类关系可以用MOCK暂时模拟,这是一个不错的解决方案。◆ 由小到大、由偏到全、统筹兼顾:一个产品或者一个项目是比较大的,所以我们这里就需要遵循由小到大、由偏到全、统筹兼顾的原则,分解功能和代码。把所有的规模大、复杂性高的工作,分解成小的任务来完成,这样既方便团队协作,同时也减轻了复杂度,使整个开发一下子变得简单了许多。◆ 保持随时重构的习惯:很多开发者在经过测试代码-功能代码-测试通过以后就当完成了任务,其实你会发现随着其他功能的引入或者使用过程中发现了很多重复、冗余的代 码、再或者先前的代码结构和设计不太合理,这个时候就需要随时的进行重构和单元测试,在一方面可以避免产生风险,另一方面可以使系统更加完善。◆ 随时进行回归:在”测试代码-功能代码-测试-重构“的循环中一定要记住多回归,因为这样可以保证当前的代码是不是会影响到前面的功能,其实只需要看看红绿灯就行。◆ 查看和统计代码覆盖率:通过前面的步骤之后,我们就要看一下实现的功能是否达到我们的预期目标,除了功能完善之外,还要保证代码的覆盖率,因为它是一个系统稳定与否、可维护性与否的一个重大标志。
◆ 单键运行方法、类、命名空间、项目和解决方案中的单元测试◆ 能够快速测试实例方法、静态方法或属性◆ 可以直接跳到.NET Reflector中的任何方法、类型、项目或引用中,这个功能提供了相当大的方便◆ 在调试过程中可以查看.NET Reflector中的任何模块或堆栈信息◆ 支持多种单元测试框架,包括NUnit、MbUnit、xUnit和MSTest◆ 测试运行在自己的进程中以消除其他问题和边际效应◆ 可以轻松对任何目标测试进行调试或执行代码覆盖率测试(比微软自带的单元测试和代码覆盖功能要好用多了)◆ 支持所有主流的.NET语言:C#、VB、C++和F#
◆ TestDriven.Net是基于.NET框架的。再由于VS 2010支持使用多个.NET版本,所以支持各个VS版本和工具就没有问题了◆ 完全支持在VS 2008和VS 2010中使用MSTest◆ 完全支持.NET Reflector 6 Pro◆ 支持NUnit 2.5.3◆ 支持和兼容VS 2005、VS 2008、VS 2010几个版本◆ 支持Silverlight 4的测试
TestDriven.NET兼容于如下VS版本:Windows XP、Vista、Windows 7、Windows 2000、Windows 2003和Windows 2008(32和64位)上的Visual Studio 2005、2008和2010。官方已经不再对VS 2003支持。
◆ 企业版:每台机器一个许可认证◆ 专业版:一般的许可形式◆ 个人版:面向学生、开源开发者和试验用户的免费许可(大家可以下载这个版本,个人感觉很好用)
1: class ObjectPoker : DependencyObject
2: {
3: //注册依赖属性property1
4: public static readonly DependencyProperty TestProp1 = DependencyProperty.Register("property1", typeof(string), typeof(ObjectPoker));
5: }
6:
7: class SubclassPoker : ObjectPoker
8: {
9: }
1: [Test]
2: [ExpectedException(typeof(ArgumentException))]
3: public void TestMultipleRegisters()
4: {
5: //测试注册相同名的依赖属性
6: DependencyProperty.Register("p1", typeof(string), typeof(ObjectPoker));
7: DependencyProperty.Register("p1", typeof(string), typeof(ObjectPoker));
8: }
1: [Test]
2: [ExpectedException(typeof(ArgumentException))]
3: public void TestMultipleAddOwner()
4: {
5: //测试AddOwner,添加相同类型的Owner
6: ObjectPoker.TestProp1.AddOwner(typeof(SubclassPoker), new PropertyMetadata());
7: ObjectPoker.TestProp1.AddOwner(typeof(SubclassPoker), new PropertyMetadata());
8: }
1: [Test]
2: public void TestDefaultMetadata()
3: {
4: //测试默认元数据
5: DependencyProperty p;
6: p = DependencyProperty.Register("TestDefaultMetadata1", typeof(string), typeof(ObjectPoker));
7: Assert.IsNotNull(p.DefaultMetadata);
8:
9: //测试元数据的默认值
10: p = DependencyProperty.Register("TestDefaultMetadata2", typeof(string), typeof(ObjectPoker), new PropertyMetadata("hi"));
11: Assert.IsNotNull(p.DefaultMetadata);
12: Assert.AreEqual("hi", p.DefaultMetadata.DefaultValue);
13: }
1: [Test]
2: public void TestAddOwnerNullMetadata()
3: {
4: //首先注册一个依赖属性,然后再AddOwner,最后根据新的Owner获取元数据
5: DependencyProperty p = DependencyProperty.Register("TestAddOwnerNullMetadata", typeof(string), typeof(ObjectPoker));
6: p.AddOwner(typeof(SubclassPoker), null);
7:
8: PropertyMetadata pm = p.GetMetadata(typeof(SubclassPoker));
9: Assert.IsNotNull(pm);
10: }
1: //首先注册一个依赖属性,然后再OverrideMetadata
2: [Test]
3: [ExpectedException(typeof(ArgumentNullException))]
4: public void TestOverrideMetadataNullMetadata()
5: {
6: //有Type但PropertyMetadata为null时,OverrideMetadata操作
7: DependencyProperty p = DependencyProperty.Register("TestOverrideMetadataNullMetadata", typeof(string), typeof(ObjectPoker));
8: p.OverrideMetadata(typeof(SubclassPoker), null);
9: }
1: [Test]
2: [ExpectedException(typeof(ArgumentNullException))]
3: public void TestOverrideMetadataNullType()
4: {
5: //当Type为null,OverrideMetadata操作
6: DependencyProperty p = DependencyProperty.Register("TestOverrideMetadataNullType", typeof(string), typeof(ObjectPoker));
7: p.OverrideMetadata(null, new PropertyMetadata());
8: }
1: [Test]
2: [ExpectedException(typeof(InvalidOperationException))]
3: public void TestReadonlyOverrideMetadata()
4: {
5: //通过DependencyPropertyKey的方式OverrideMetadata
6: DependencyPropertyKey ro_key = DependencyProperty.RegisterReadOnly("readonly-prop1",
7: typeof(double),
8: typeof(ObjectPoker),
9: new PropertyMetadata(double.NaN));
10: ro_key.DependencyProperty.OverrideMetadata(typeof(SubclassPoker), new PropertyMetadataPoker());
11: }
1: [Test]
2: public void TestReadonlyOverrideMetadataFromKey()
3: {
4: //通过DependencyPropertyKey的方式OverrideMetadata
5: DependencyPropertyKey ro_key = DependencyProperty.RegisterReadOnly("readonly-prop2",
6: typeof(double),
7: typeof(ObjectPoker),
8: new PropertyMetadata(double.NaN));
9: ro_key.OverrideMetadata(typeof(SubclassPoker), new PropertyMetadataPoker());
10: }
1:
2: using System.Collections.Generic;
3: namespace System.Windows
4: {
5: public sealed class DependencyProperty
6: {
7: //一个依赖属性可能有多个所有者,所以根据每个所有者都有自己的元数据
8: private Dictionary<Type,PropertyMetadata> metadataByType = new Dictionary<Type,PropertyMetadata>();
9:
10: //声明一个UnsetValue
11: public static readonly object UnsetValue = new object ();
12:
13: //依赖属性私有构造函数,作为初始化操作,每个依赖属性在注册的时候都会调用并初始化数据
14: private DependencyProperty (bool isAttached, string name, Type propertyType, Type ownerType,
15: PropertyMetadata defaultMetadata,
16: ValidateValueCallback validateValueCallback)
17: {
18: IsAttached = isAttached;
19: DefaultMetadata = (defaultMetadata == null ? new PropertyMetadata() : defaultMetadata);
20: Name = name;
21: OwnerType = ownerType;
22: PropertyType = propertyType;
23: ValidateValueCallback = validateValueCallback;
24: }
25:
26: internal bool IsAttached { get; set; }
27: public bool ReadOnly { get; private set; }
28: public PropertyMetadata DefaultMetadata { get; private set; }
29: public string Name { get; private set; }
30: public Type OwnerType { get; private set; }
31: public Type PropertyType { get; private set; }
32: public ValidateValueCallback ValidateValueCallback { get; private set; }
33:
34: //获取依赖属性的编号,暂未实现,在上一篇“WPF基础到企业应用系列7——深入剖析依赖属性”有实现,原理是在初始化的时候++
35: public int GlobalIndex {
36: get { throw new NotImplementedException (); }
37: }
38:
39: //传入ownerType增加Owner
40: public DependencyProperty AddOwner(Type ownerType)
41: {
42: return AddOwner (ownerType, null);
43: }
44:
45: //增加所有者,根据ownerType和typeMetadata
46: public DependencyProperty AddOwner(Type ownerType, PropertyMetadata typeMetadata)
47: {
48: if (typeMetadata == null) typeMetadata = new PropertyMetadata ();
49: OverrideMetadata (ownerType, typeMetadata);
50:
51: // MS seems to always return the same DependencyProperty
52: return this;
53: }
54:
55: //获取元数据,依据forType
56: public PropertyMetadata GetMetadata(Type forType)
57: {
58: if (metadataByType.ContainsKey (forType))
59: return metadataByType[forType];
60: return null;
61: }
62:
63: //获取元数据,依据该依赖属性
64: public PropertyMetadata GetMetadata(DependencyObject d)
65: {
66: if (metadataByType.ContainsKey (d.GetType()))
67: return metadataByType[d.GetType()];
68: return null;
69: }
70:
71: //获取元数据,依据dependencyObjectType
72: public PropertyMetadata GetMetadata(DependencyObjectType dependencyObjectType)
73: {
74: if (metadataByType.ContainsKey (dependencyObjectType.SystemType))
75: return metadataByType[dependencyObjectType.SystemType];
76: return null;
77: }
78:
79: //验证类型是否有效
80: public bool IsValidType(object value)
81: {
82: return PropertyType.IsInstanceOfType (value);
83: }
84:
85: //验证值是否有效
86: public bool IsValidValue(object value)
87: {
88: if (!IsValidType (value))
89: return false;
90: if (ValidateValueCallback == null)
91: return true;
92: return ValidateValueCallback (value);
93: }
94:
95: //重写元数据,使用PropertyMetadata类的DoMerge方法来操作
96: public void OverrideMetadata(Type forType, PropertyMetadata typeMetadata)
97: {
98: if (forType == null)
99: throw new ArgumentNullException ("forType");
100: if (typeMetadata == null)
101: throw new ArgumentNullException ("typeMetadata");
102:
103: if (ReadOnly)
104: throw new InvalidOperationException (String.Format ("Cannot override metadata on readonly property '{0}' without using a DependencyPropertyKey", Name));
105:
106: typeMetadata.DoMerge (DefaultMetadata, this, forType);
107: metadataByType.Add (forType, typeMetadata);
108: }
109:
110: //重写元数据,使用PropertyMetadata类的DoMerge方法来操作
111: public void OverrideMetadata (Type forType, PropertyMetadata typeMetadata, DependencyPropertyKey key)
112: {
113: if (forType == null)
114: throw new ArgumentNullException ("forType");
115: if (typeMetadata == null)
116: throw new ArgumentNullException ("typeMetadata");
117:
118: typeMetadata.DoMerge (DefaultMetadata, this, forType);
119: metadataByType.Add (forType, typeMetadata);
120: }
121:
122: public override string ToString ()
123: {
124: return Name;
125: }
126:
127: //得到哈希值,区别不同的依赖属性,Name、PropertyType、OwnerType的哈希值取异
128: public override int GetHashCode ()
129: {
130: return Name.GetHashCode() ^ PropertyType.GetHashCode() ^ OwnerType.GetHashCode();
131: }
132:
133: //注册依赖属性(参数:依赖属性名、依赖属性的Type、拥有者的Type)
134: public static DependencyProperty Register(string name, Type propertyType, Type ownerType)
135: {
136: return Register(name, propertyType, ownerType, null, null);
137: }
138:
139: //注册依赖属性(参数:依赖属性名、依赖属性的Type、拥有者的Type、元数据)
140: public static DependencyProperty Register(string name, Type propertyType, Type ownerType,
141: PropertyMetadata typeMetadata)
142: {
143: return Register(name, propertyType, ownerType, typeMetadata, null);
144: }
145:
146: //注册依赖属性(参数:依赖属性名、依赖属性的Type、拥有者的Type、元数据、验证回调委托)
147: public static DependencyProperty Register(string name, Type propertyType, Type ownerType,
148: PropertyMetadata typeMetadata,
149: ValidateValueCallback validateValueCallback)
150: {
151: if (typeMetadata == null)
152: typeMetadata = new PropertyMetadata();
153:
154: DependencyProperty dp = new DependencyProperty(false, name, propertyType, ownerType,
155: typeMetadata, validateValueCallback);
156: DependencyObject.register(ownerType, dp);
157:
158: dp.OverrideMetadata (ownerType, typeMetadata);
159:
160: return dp;
161: }
162:
163: //注册附加依赖属性(参数:依赖属性名、依赖属性的Type、拥有者的Type)
164: public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType)
165: {
166: return RegisterAttached(name, propertyType, ownerType, null, null);
167: }
168:
169: //注册附加依赖属性(参数:依赖属性名、依赖属性的Type、拥有者的Type、元数据)
170: public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType,
171: PropertyMetadata defaultMetadata)
172: {
173: return RegisterAttached(name, propertyType, ownerType, defaultMetadata, null);
174: }
175:
176: //注册附加依赖属性(参数:依赖属性名、依赖属性的Type、拥有者的Type、元数据、验证回调委托)
177: public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType,
178: PropertyMetadata defaultMetadata,
179: ValidateValueCallback validateValueCallback)
180: {
181: DependencyProperty dp = new DependencyProperty(true, name, propertyType, ownerType,
182: defaultMetadata, validateValueCallback);
183: DependencyObject.register(ownerType, dp);
184: return dp;
185: }
186:
187: //注册只读依赖属性,暂未实现
188: public static DependencyPropertyKey RegisterAttachedReadOnly(string name, Type propertyType, Type ownerType,
189: PropertyMetadata defaultMetadata)
190: {
191: throw new NotImplementedException("RegisterAttachedReadOnly(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata)");
192: }
193:
194: //注册只读依赖属性,暂未实现
195: public static DependencyPropertyKey RegisterAttachedReadOnly(string name, Type propertyType, Type ownerType,
196: PropertyMetadata defaultMetadata,
197: ValidateValueCallback validateValueCallback)
198: {
199: throw new NotImplementedException("RegisterAttachedReadOnly(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)");
200: }
201:
202: //注册只读依赖属性(参数:依赖属性名、依赖属性的Type、拥有者的Type、元数据)
203: public static DependencyPropertyKey RegisterReadOnly(string name, Type propertyType, Type ownerType,
204: PropertyMetadata typeMetadata)
205: {
206: return RegisterReadOnly (name, propertyType, ownerType, typeMetadata, null);
207: }
208:
209: //注册只读依赖属性(参数:依赖属性名、依赖属性的Type、拥有者的Type、元数据、验证回调委托)
210: public static DependencyPropertyKey RegisterReadOnly(string name, Type propertyType, Type ownerType,
211: PropertyMetadata typeMetadata,
212: ValidateValueCallback validateValueCallback)
213: {
214: DependencyProperty prop = Register (name, propertyType, ownerType, typeMetadata, validateValueCallback);
215: prop.ReadOnly = true;
216: return new DependencyPropertyKey (prop);
217: }
218:
219: }
220: }