2010-10-01 86 views
39

閱讀this question幫助我鞏固了我一直使用單元測試的一些問題,TDD等。單元測試/ TDD有用的設計模式?

自從接觸TDD發展方法以來,我知道這是正確的道路。閱讀各種教程幫助我瞭解如何開始,但他們一直非常簡單 - 並非真正適用於活躍項目的東西。我所管理的最好的方法是圍繞我的代碼的一小部分編寫測試 - 像庫,這些東西被主應用程序使用,但沒有以任何方式集成。雖然這很有用,但它相當於代碼庫的5%左右。關於如何進入下一步,有很少的東西,以幫助我對主應用程序進行一些測試。

諸如「Most code without unit tests is built with hard dependencies (i.e.'s new's all over the place) or static methods.」和「...it's not rare to have a high level of coupling between classes, hard-to-configure objects inside your class [...] and so on.」等評論讓我意識到下一步是理解如何分離代碼以使其可測試。

我應該看什麼來幫助我做到這一點?是否有一套特定的設計模式,我需要理解並開始實施,這將允許更輕鬆的測試?

回答

45

這裏邁克·克利夫頓介紹了24種測試模式,從2004年它是一個有用的啓發設計單元測試時。

http://www.codeproject.com/Articles/5772/Advanced-Unit-Test-Part-V-Unit-Test-Patterns

合格/不合格模式

這些模式是你的第一道防線(或攻擊,這取決於你的觀點),以保證良好的代碼。但要注意的是,他們在代碼中告訴你的是欺騙性的。

  • 的簡單測試式樣
  • 的代碼路徑圖案
  • 的參數震盪格局

數據交易模式

數據交易模式是在擁抱開始數據持久性和溝通的問題。有關此主題的更多信息將在「模擬模式」下討論。另外,這些模式有意忽略壓力測試,例如在服務器上加載。這將在「壓力測試模式」下討論。

  • 的簡單數據I/O模式
  • 該約束的數據模式
  • 回滾模式

徵收管理模式

很多的哪些應用程序做管理信息的集合。儘管程序員可以使用各種各樣的集合,但驗證(並因此證明)代碼使用正確的集合非常重要。這會影響順序和約束。

  • 收集,整理格局
  • 枚舉格局
  • 收藏約束模式
  • 收集,索引模式

性能模式

單元測試應該不只是關心功能,而且與形式。被測代碼如何有效地執行其功能?多快?它使用多少內存?它是否有效地爲數據檢索交換數據插入?它是否正確地釋放資源?這些都是單元測試範圍內的事情。通過在單元測試中包含性能模式,實現者可以達到目標,從而獲得更好的代碼,更好的應用程序和更快樂的客戶。

  • 性能測試式樣

過程模式

單元測試旨在測試,那麼,單位...基本的應用程序的功能。可以說,測試過程應該被降級到驗收測試程序,但是我並不贊成這種說法。一個過程只是一個不同類型的單位。使用單元測試器進行測試可以提供與其他單元測試相同的優勢 - 它記錄了過程的工作方式,並且單元測試人員可以通過按順序測試過程來快速識別潛在的用戶界面問題,從而幫助實施人員好。術語「過程」還包括狀態轉換和業務規則,兩者都必須經過驗證。

  • 進程序格局
  • 過程狀態模式
  • 的流程,規則模式

模擬模式

數據處理,難以測試,因爲他們往往需要預設配置,開放連接和/或在線設備(僅舉幾例)。模擬對象可以通過模擬代碼進行交易的數據庫,Web服務,用戶事件,連接和/或硬件來拯救。 Mock對象還可以建立故障情形是很難在現實世界中繁殖能力 - 有損連接,一個緩慢的服務器,一個失敗的網絡集線器等

  • 實體模型對象模式
  • 在服務模式模擬
  • 誤碼仿真模式
  • 組件仿真模​​式

多線程模式

單元測試多線程應用程序可能是最困難的事情之一,因爲您必須設置一種條件,其本質意圖是異步的,因此是非確定性的。這個主題本身可能是一篇重要的文章,所以我在這裏只提供一個非常通用的模式。此外,爲了正確地執行許多線程的測試中,測試器單元應用程序必須本身執行測試作爲分開的線程,使得單元測試儀沒有被禁用當一個線程處於等待狀態

  • 用信號發送的模式結束
  • 死鎖分辨率模式

壓力測試模式

大多數應用程序都在理想的環境下進行試驗 - 程序員使用快速MACHIN幾乎沒有網絡流量,使用小數據集。現實世界是非常不同的。在某些事情完全中斷之前,應用程序可能會遭受降級,並且對用戶反應不佳或出錯。驗證代碼在壓力下的性能的單元測試應該在理想環境中以相同的熱情(如果不是更多)滿足單元測試。

  • 大容量數據的壓力測試模式
  • 資源,應力測試圖案
  • 的加載測試式樣

表示層模式

之一單元測試最具挑戰性的方面是驗證信息在表現層本身以及內部工作中獲得用戶權利應用程序的國王正確設置表示層狀態。通常,表示層與業務對象,數據對象和控制邏輯糾纏在一起。如果您打算對錶示層進行單元測試,那麼您必須認識到,必須清楚分離關注點。部分解決方案涉及開發適當的模型 - 視圖 - 控制器(MVC)架構。 MVC體系結構提供了在使用表示層時開發良好設計實踐的手段。但是,它很容易被濫用。需要一定的紀律來確保你實際上正確地實現了MVC架構,而不僅僅是單純的用詞。

  • 視圖狀態測試圖案
  • 模型 - 國家測試圖案
2

設計模式與TDD沒有直接關係,因爲它們是實現細節。您不應該僅僅因爲它們存在而試圖將代碼放入代碼中,而是傾向於隨代碼的演變而出現。如果你的代碼很臭,它們也會變得有用,因爲它們有助於解決這些問題。不要在設計模式中開發代碼,只需編寫代碼。然後讓測試通過,然後重構。

1

很多像這樣的問題都可以通過適當的封裝來解決。或者,如果你正在混淆你的擔憂,你可能會遇到這個問題。假設你有驗證用戶的代碼,驗證域對象,然後將域對象全部保存在一個方法或類中。你混淆了你的擔憂,你不會很高興。您需要分離這些問題(認證/授權,業務邏輯,持久性),以便您可以單獨測試它們。

設計模式很有幫助,但很多外來設計模式都有很窄的問題,可以應用。像複合,命令這樣的模式經常被使用,並且很簡單。

指導原則是:如果很難測試某些東西,可以將其重構爲較小的問題並單獨測試重構的位。所以如果你有一個包含5級if語句和幾個for循環的200行方法,你可能想要打破這個糟糕的局面。

因此,首先看看您是否可以通過分離您的擔憂來簡化複雜代碼,然後查看是否可以通過分解簡化代碼來簡化代碼。當然,如果設計模式突然出現在你身上,那就去做吧。

9

我說你需要的主要是兩件事情來測試,他們結伴而行:

  • 接口,接口,接口
  • 依賴注入;這將與接口一起幫助您隨意交換部件以隔離要測試的模塊。你想測試你的類似cron的系統發送通知給其他服務?對它進行實例化並將實際代碼實現替換爲遵循正確接口的組件,但硬連線以您想要測試的方式做出反應:郵件通知?測試時,SMTP服務器是向下扔

我自己還沒有掌握單元測試的藝術異常(我遠離它),但是這是我的主要精力會目前正在發生的事情。問題是,我仍然沒有設計測試,因此我的代碼不得不向後彎曲以適應...

6

Michael Feather的書Working Effectively With Legacy Code正是你要找的。他將遺留代碼定義爲'沒有測試的代碼',並討論如何使其得到測試。

與大多數情況一樣,它一次只能一步。當您進行更改或修復時,請嘗試增加測試覆蓋率。隨着時間的推移,你會有一套更完整的測試。它討論了減少耦合以及如何在應用程序邏輯之間進行測試的技巧。

在其他的答案依賴注入注意到是編寫可測試(和一般的鬆耦合)碼的一個好方法。

3

傑拉德·梅薩羅斯的xUnit測試模式:重構測試代碼是座充滿模式進行單元測試。我知道你在尋找TDD上的圖案,但我認爲你會在這本書中找到很多有用的材料。

這本書是在safari中,所以你可以很好地看看裏面是什麼,看看它是否可能是有幫助的: http://my.safaribooksonline.com/9780131495050

2

已經讓我意識到,下一步是瞭解如何進行去耦代碼,使其可測試。

我應該看什麼來幫助我做到這一點?是否有一套特定的設計模式,我需要理解並開始實施,這將允許更輕鬆的測試?

對上! SOLID是你在找什麼(是的,真的)。我一直在推薦這些2 ebooks,特別是關於SOLID的問題。

您還必須瞭解,如果您要將單元測試引入現有代碼庫,那麼它非常困難。不幸的是,緊密耦合的代碼太常見了。這並不意味着不這樣做,但是一段時間就會像你剛剛提到的那樣,測試將更多地集中在小塊中。

隨着時間的推移,這些成長爲一個更大的範圍,但它不依賴於現有的代碼庫的大小,而不是添加的問題,團隊的大小和多少實際上這樣做。

0

依賴注入/ IoC。還要閱讀依賴注入框架,如SpringFramework和google-guice。他們還針對如何編寫可測試的代碼。

1

安排,法,斷言是幫助你構建圍繞具體的測試代碼的模式的一個很好的例子用例。

下面是一些假設的C#代碼,演示該模式。

[TestFixture] 
public class TestSomeUseCases() { 

    // Service we want to test 
    private TestableServiceImplementation service; 

    // IoC-injected mock of service that's needed for TestableServiceImplementation 
    private Mock<ISomeService> dependencyMock; 

    public void Arrange() { 
     // Create a mock of auxiliary service 
     dependencyMock = new Mock<ISomeService>(); 
     dependencyMock.Setup(s => s.GetFirstNumber(It.IsAny<int>)).Return(1); 

     // Create a tested service and inject the mock instance 
     service = new TestableServiceImplementation(dependencyMock.Object); 
    } 

    public void Act() { 
     service.ProcessFirstNumber(); 
    } 

    [SetUp] 
    public void Setup() { 
     Arrange(); 
     Act(); 
    } 

    [Test] 
    public void Assert_That_First_Number_Was_Processed() { 
     dependencyMock.Verify(d => d.GetFirstNumber(It.IsAny<int>()), Times.Exactly(1)); 
    } 
} 

如果你有很多的場景進行測試,你可以提取與混凝土共同抽象類安排&法案位(或只是安排),並執行餘下的抽象位繼承的類,組&測試功能測試功能。