VS2012 Unit Test —— 我对接口进行单元测试使用的技巧
【题外话】
对单元测试不熟悉的童鞋可参照我之前写过的两篇博文:
《VS2012 单元测试之泛型类(Generics Unit Test)》。
以下Demo将使用我已写好的一些关于单元测试的类库(已放至 https://idletest.codeplex.com/
,其用了大量的匿名方法,同样不熟悉的可参照我上一篇博文《委托的N种写法,你喜欢哪种?》)。
【进入正题】
与其说对接口测试还不如说针对抽象测试,也是我个人比较喜欢的编码步骤:编写接口(面向抽象)=>单元测试=>实现。
OK here we go...
首先假设有如下接口(这也是从我的代码中提取出来的)
public interface ITextFormatter { /// <summary> /// 根据指定格式字符串获取对象 /// </summary> T GetObject<T>(string formatString); /// <summary> /// 将对象转化为指定格式字符串 /// </summary> string GetString(object obj); }
那么接下来我要做的并不是马上去实现它,而是测试先行,我将编写如下测试代码
public abstract class BaseFormatterTest { protected abstract ITextFormatter formatter { get; } /// <summary> ///GetObject 的测试 ///</summary> public void GetObjectTestHelper<T>() { AssertCommon.AssertIsNull<string, T>(TestCommon.GetEmptyStrings(), true, (string strTemp) => { return formatter.GetObject<T>(strTemp); }); EntityParameter ep = new EntityParameter(123); string str = formatter.GetString(ep); EntityParameter actual = formatter.GetObject<EntityParameter>(str); Assert.IsNotNull(actual); Assert.AreEqual<int>(123, actual.Data); } public virtual void GetObjectTest() { GetObjectTestHelper<GenericParameterHelper>(); } /// <summary> ///GetString 的测试 ///</summary> public virtual void GetStringTest() { AssertCommon.AssertEmpty<string>(formatter.GetString(null)); AssertCommon.AssertEmpty<string>(formatter.GetString(new object()), false); string actual = formatter.GetString(new EntityParameter(12)); AssertCommon.AssertEmpty<string>(actual, false); EntityParameter entity = formatter.GetObject<EntityParameter>(actual); Assert.AreEqual<int>(12, entity.Data); }
值得注意的是以上方法中包含了一个叫做AssertCommon类,这个是我自己定义的,源码在 https://idletest.codeplex.com/。
我现在通过Xml序列化实现字符串与对象的相互转化,所以我继承这个基类来编写具体的测试,当然此时的测试代码就很简单了。
/// <summary> ///这是 ITextFormatterTest 的测试类,旨在 ///包含所有 ITextFormatterTest 单元测试 ///</summary> [TestClass()] public class XmlFormatterTest : BaseFormatterTest { [TestMethod()] public override void GetObjectTest() { base.GetObjectTest(); } /// <summary> ///GetString 的测试 ///</summary> [TestMethod] public override void GetStringTest() { base.GetStringTest(); } protected override ITextFormatter formatter { get { return new XmlFormatter(); } } }
完成测试编码后开始实现具体代码,我这里将创建一个抽象基类和一个子类去实现需要的功能
public abstract class BaseFormatter { private Encoding encoding; public Encoding Encoding { get { return this.encoding; } } public BaseFormatter(Encoding encoding) { this.encoding = encoding; } }
public class XmlFormatter : BaseFormatter, ITextFormatter { public XmlFormatter() : base(Encoding.Default) { } public XmlFormatter(Encoding encoding) : base(encoding) { } /// <summary> /// 将对象序列化为XML格式字符串 /// </summary> /// <param name="obj"></param> /// <returns></returns> public string GetString(object obj) { string result = string.Empty; if (obj ==null) { return result; } XmlSerializer serializer =new XmlSerializer(obj.GetType()); using (MemoryStream stream =new MemoryStream()) { serializer.Serialize(stream, obj); byte[] buffer = stream.ToArray(); result = this.Encoding.GetString(buffer); stream.Close(); } return result; } ///<summary> /// 将XML格式字符串转化为对象 ///</summary> public T GetObject<T>(string xmlString) { T obj = default(T); if (string.IsNullOrEmpty(xmlString)) { return obj; } XmlSerializer serializer =new XmlSerializer(typeof(T)); using (MemoryStream stream = new MemoryStream(this.Encoding.GetBytes(xmlString))) { obj = (T)serializer.Deserialize(stream); stream.Close(); } return obj; } }
整个过程就这样子,当然中间肯定运行多次测试来即时修正编码过程中的错误。篇幅有限,我在这里并没有编写构造函数的测试方法。
【后话】
如此一来以后再添加对接口的更多实现时,保证了不再需要编写大量重复的测试代码,例如再增加类JsonFormatter时就轻而易举的完成它的单元测试,后期开发效率与质量均得到保证。
编写完毕,借此抛砖引玉,希望得到您更好的意见,鲜花鸡蛋均欢迎。