------------------
前言
Preface
------------------
這幾天正在做dynamic的時候,突然想到了測試架構的最新思路。
------------------
思路介紹
------------------
整個測試流程主要是:給出測試資料,查看符合的結果。
測試種類非常多,例如CS介面測試(使用鉤子等錄製並復原);BS介面測試(使用js直接調用) ;網站的測試(類比http);普通類庫測試(NUnit等)等等。
如果是介面這種黑箱測試,我就不討論;網站測試難度很大,應該盡量化簡為類庫測試;所以最終來到普通來庫測試。
因此一個非常典型的測試代碼如下:
代碼 public void test011()
{
ObjectWithInterfaceCollection pojo4 = new ObjectWithInterfaceCollection();
pojo4.Age = 321;
pojo4.Fee = 321.321;
pojo4.Name = "pojo";
pojo4.Type = EnumTYpe.B;
ObjectWithInterfaceCollection subpojo = new ObjectWithInterfaceCollection();
subpojo.Age = 456;
subpojo.Pojo = new ObjectWithCollecton();
subpojo.Pojo.Age = 789;
pojo4.Pojos = new ObjectWithCollecton[] { subpojo };
pojo4.Pojo = subpojo;
pojo4.Ipojolist = new List<IinterfaceWithValue>();
pojo4.Ipojolist.Add(subpojo);
pojo4.Ipojo = subpojo;
IXmlNode node = XmlManager.DynamicSerialize(pojo4);
ObjectWithInterfaceCollection rpojo4 = XmlManager.DynamicDeserialize<ObjectWithInterfaceCollection>(node.Serialize(true));
Assert.IsEqual(pojo4.Age, rpojo4.Age);
Assert.IsEqual(pojo4.Fee, rpojo4.Fee);
Assert.IsEqual(pojo4.Name, rpojo4.Name);
Assert.IsEqual(pojo4.Type, rpojo4.Type);
Assert.IsEqual(pojo4.Pojos[0].Age, rpojo4.Pojos[0].Age);
Assert.IsEqual(pojo4.Pojos[0].Pojo.Age, rpojo4.Pojos[0].Pojo.Age);
Assert.IsEqual(pojo4.Pojo.Age, rpojo4.Pojo.Age);
Assert.IsEqual(pojo4.Pojo.Pojo.Age, rpojo4.Pojo.Pojo.Age);
Assert.IsEqual(pojo4.Ipojolist[0].Age, rpojo4.Ipojolist[0].Age);
Assert.IsEqual(pojo4.Ipojo.Age, rpojo4.Ipojo.Age);
Assert.Write(node.Serialize(true));
}
現在的測試存在的問題:
1. 由於測試代碼和邏輯代碼相互關聯非常緊密;一旦邏輯代碼變動,導致測試代碼變動的非常厲害,甚至要重寫。
2. 測試代碼如果書寫不認真,那麼在第一個問題上就會問題加重;可是如果寫測試代碼也使用了重構、設計模式,這樣工作量倍增,一旦邏輯代碼變動,測試代碼變得沒有意義。
可見,如果要使用測試驅動,那麼帶來的問題是非常多的。
測試驅動能解決的問題是:
1. 通過迴歸測試,能夠知道新開發的功能有沒有違反原有的功能。
貌似測試驅動能解決的問題和帶來的問題相比較,微不足道。
因為測試的正確取決與輸入參數的正確和全面。可是如果對測試代碼馬虎對待,那麼結果一定也有問題。這樣導致了迴歸測試出現錯誤。產生一個惡性迴圈。所以到目前為止,我還沒想到一個好的思路。不過有點轉機。
由於測試代碼是邏輯代碼的輔助代碼,從理論上,邏輯代碼變更後,測試代碼應該非常容易變更。而目前無法做到,原因是,對整個測試流程無法抽象出一個簡單的模型。我現在就嘗試抽象。
上文的程式碼封裝含了:
1. 產生測試資料
複雜、煩瑣、而且重要。測試資料直接決定了本測試結果是否有效。因此這部分一定需要人去寫。
測試資料本身一定也是POJO對象(參考3);同時測試中,必須可見方便維護和分析。
2. 調用對應的方法
簡單,僅僅一句話。完全可以機器代替,所以問題在於手寫一個調用快,還是滑鼠指定一個調用快。
3. 查看測試資料是否符合期望。
工作量煩瑣,而且毫無意義,所以很多人喜歡用console.write,但是這樣又丟失了機器檢測的優勢。導致人為錯誤。
可是分析發現,測試資料一定是對象,例如string, 如果是類的對象,則一定需要繼續展開,查看這個類的屬性;因此可以看成是一個典型的POJO模型。
如果有一種機制,能夠對POJO進行展開和檢查,而且不需要人為參與,那麼就降低了很多工作量。
唯一需要人為參與的地方,就是規定出某個變數 = XXXX / is null / throw exception. 這個規定的過程,可以抽象出來一種機制。
根據分析,那麼到底是使用IDE工具?還是使用代碼?
從程式員角度分析,使用代碼>使用滑鼠+鍵盤輸入。!!
結論:盡量使用代碼。
資料輸入使用介面還是代碼?
如果使用介面,那麼接下來的也要介面。非常麻煩。僅僅制定一個對象,可能需要寫一堆xml
結論:使用代碼。
結果驗證使用介面還是代碼?
結果驗證的時候,第一次需要配置驗證,可以參考noebe.global的思路,自動對輸出進行截取+編譯為xml,給出介面使用者指定資料校正。
之後如果再運行,就可以查看是否和第一次制定的一致。類似的效果:
結論:Assert.Verify(object value); <---- 一句話就把所有結果都驗證了。
配置的檔案放在什麼地方?
放在項目的目錄中。可以使用codelive.visual,也可以直接操作目錄。目前第一個開放版本使用file。不整合在IDE。
依賴性到底如何?
1. 是否使用reflection? 如果不使用,那麼就用reflection。這樣可以不依賴reflection的dll
2. 是否使用config?如果不進行序列化等操作,可以不用——》必須建立一個可以在核心架構解析的文法,例如:
xxx.xxx.xxx = xxx;
如果這2個問題解決了,那麼整個Assert.Verify可以脫離framework直接運行。
小結一下:
之前所有代碼都不變,例如attribute之類的。可以使用自己的,也可以使用testdriven的。
然後來到驗證的時候,首先擷取當前method的一個id,作為搜尋;
如果發現文檔不存在,則拋出異常。使用者可以調用Assert.Save()建立一個驗證文檔。這個時候將遞迴擷取所有資料。
搜尋到驗證文檔後,開始解析 並對比值是否正確。