【題外話】
對單元測試不熟悉的童鞋可參照我之前寫過的兩篇博文:
《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時就輕而易舉的完成它的單元測試,後期開發效率與質量均得到保證。
編寫完畢,藉此拋磚引玉,希望得到您更好的意見,鮮花雞蛋均歡迎。