MockTransaction mockTransaction; |
該解決方案產生了一個稍長的測試,但該測試只關注正在測試的類的直接行為,
而不是 ATM 接口之外整個系統的行為。也就是說,我們不再檢查測試帳戶的最終余額是否正確;
我們將在對
Transaction
對象的單元測試中檢查該函數,而不是在對
AtmGui
對象的單元測試中。
注:根據模仿對象的創造者所說,它應該在其
validate()
方法內部執行自己的所有驗證。
在本示例中,為了清晰起見,我們將驗證的某些部分放在了測試方法內部。
隨著您更加熟練地使用模仿對象,對于將多少驗證職責代理給模仿對象,您將會深有體會。
![]() ![]() |
![]()
|
在清單 6 中,我們使用了
AtmGui
的匿名內部子類來覆蓋
createTransaction
方法。
因為我們只需要覆蓋一個簡單的方法,所以這是實現我們目標的簡明方法。
如果我們覆蓋多個方法或在許多測試之間共享
AtmGui
子類,那么創建一個完整的(非匿名)成員類是值得的。
我們還使用了實例變量來存儲對模仿對象的引用。這是在測試方法和特殊化類之間共享數據的最簡單方法。這是可以接受的,因為我們的測試框架不是多線程的或可重入的。(如果它是多線程的或可重入的,則必須用
synchronized
塊保護我們自己。)
最后,我們將模仿對象本身定義為測試類的專用內部類 ― 這通常是一種便利的方法, 因為將模仿對象就放在使用它的測試代碼旁邊會更加清楚,又因為內部類有權訪問包含它們的類的實例變量。
![]() ![]() |
![]()
|
因為我們覆蓋了工廠方法來編寫這個測試,所以其結果是:我們的測試不再包括任何 原始創建代碼(現在它在基類的工廠方法內部)。 添加確實包括該代碼的測試也許是有益的。這與調用基類的工廠方法并斷言返回對象具有正確類型一樣簡單。例如:
AtmGui atm = new AtmGui(); |
注:相反,
assertTrue(t instanceof Transaction)
不能滿足,因為
MockTransaction
也是
Transaction
。
![]() ![]() |
![]()
|
此時,您可能很想更進一步并用成熟的抽象工廠對象替換工廠方法, 如 Erich Gamma 等人在 設計模式中詳細描述的那樣。(請參閱 參考資料)。 實際上,許多人已經用工廠對象來著手這種方法,而不是用工廠方法 ― 我們以前是這樣做的,但很快就放棄了。
將第三種對象類型(角色)引入系統會有一些潛在的缺點:
- 它增加了復雜性,而沒有相應地增加功能。
- 它會迫使您更改目標對象的公用接口。如果必須傳入抽象工廠對象,那么您必須添加一個新的公用構造函數或賦值(mutator)方法。
- 許多語言對于“工廠”這一概念都附有一些約定,它們會使您誤入歧途。例如,在 Java 語言中,工廠通常作為靜態方法實現;在這種情況下,這是不合適的。
請記住,本練習的宗旨是使對象更易于 測試。通常, 用于可測性的設計可以將對象的 API 推向一種更清晰更模塊化的狀態。但它會走得太遠。測試驅動的設計更改不應該污染原始對象的公用接口。
在 ATM 示例中,對于產品代碼,
AtmGui
對象始終只產生一種類型的
Transaction
對象(實際類型)。測試代碼希望它產生另一種類型的對象(模仿對象)。但強迫公用 API 適應工廠對象或抽象工廠(只因為測試代碼要求它這樣)是錯誤的設計。
如果產品代碼無需實例化該合作者的多個類型,那么添加該功能將使最終的設計不必要地變得難于理解。
文章來源于領測軟件測試網 http://www.kjueaiud.com/