這個系列,請見
單元測試(1): 什麼是單元測試
單元測試(2): 單元測試的好與壞
單元測試(3): 單元測試的好與壞2
上次首次提到了「可測試性」的重要,這一次舉另外一個例子。
這段程式,是一個將一段xml寫到一個特定目錄的檔案。
using System; using System.IO; using System.Xml.Linq; namespace ClassLibrary2 { public class Class1 { public void WriteXmlToFile(DateTime date) { XElement el = new XElement("Order", new XElement("OrderDate", date)); File.WriteAllText(@"c:\temp\result.xml", el.ToString()); } } }
相同的,如下的測試程式,要如何聲明(Assert)是否正確呢?
using System; using ClassLibrary2; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace TestProject1 { [TestClass()] public class Class1Test { [TestMethod()] public void WriteXmlToFileTest() { var target = new Class1(); target.WriteXmlToFile(new DateTime(2009, 1, 1)); } } }
真的沒辦法嗎?我們也可以將輸出的結果檔案讀出來。並與預期的結果比對。當然,這樣是不好的。
BAD
using System; using ClassLibrary2; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; namespace TestProject1 { [TestClass()] public class Class1Test { [TestMethod()] public void WriteXmlToFileTest() { var target = new Class1(); target.WriteXmlToFile(new DateTime(2009, 1, 1)); string result = File.ReadAllText(@"c:\temp\result.xml"); Assert.AreEqual(@"", result); } } } 2009-01-01T00:00:00
這樣有什麼不好呢?除了前次提到的與檔案系統相關外,又有什麼不好呢?目錄指定的路徑是客戶指定的,除可以改設定到 config 檔上,其餘沒有什麼好談的。
其實,這一段的確是可以測試的。但需要強調的事情是:可測試性不佳。
如果客戶要求的是將 xml 資料以 email 或 web service 方式傳出去呢?那要如何測試該段 xml 是正確的呢?難道要去讀對方 web service 是否收了了訊息,或 email server 是否收了 email?
我的解答如下:改變 method 的 signature
using System; using System.IO; using System.Xml.Linq; namespace ClassLibrary2 { public class Class1 { public string WriteXmlToFile(DateTime date) { XElement el = new XElement("Order", new XElement("OrderDate", date)); string result = el.ToString(); File.WriteAllText(@"c:\temp\result.xml", result); return result; } } }測試程式碼如下:
using System; using ClassLibrary2; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; namespace TestProject1 { [TestClass()] public class Class1Test { [TestMethod()] public void WriteXmlToFileTest() { var target = new Class1(); string result = target.WriteXmlToFile(new DateTime(2009, 1, 1)); Assert.AreEqual(@"", result); } } } 2009-01-01T00:00:00
只有一點小改變:該method 回傳 xml 的結果。
質疑聲:「什麼?為什麼要回傳 xml 呢?又不需要!只會浪費時間,效能變差」
這一點,真的不好回答。回到一個問題,什麼是需求?什麼樣的事情才是需求?
我們常將需求分成「功能需求」與「非功能需求」。功能需求是指客戶真的需要的功能。非功能需求又可再細分安全性、效能、可維護性、…等。
為了安全性,會加上一堆的 System.Security 的類別與物件。
為了效能,會使用多執行緒的方式加快程式執行速度、加上效能物件以量測正式機上的效能。
為了可維護性,更會以物件導向、再搞出Multi-layer, SOA, MVC 等架構。
那「可測試性」不也是一種「非功能需求」嗎?為了「可測試性」,當然也可以將某一 method 由 void 改成回傳 string 啊!
結論
為了可測試性,可以改變程式的 signature。
沒有留言:
張貼留言