qileilove

          blog已經轉移至github,大家請訪問 http://qaseven.github.io/

          邁出單元測試的第一步

           單元測試不僅是軟件行業的最佳實踐,在敏捷方法的推動下,它也成為了可持續軟件生產的支柱。根據最新的年度敏捷調查,70%的參與者會對他們的代碼進行單元測試。

            單元測試和其他敏捷實踐密切相關,所以開始編寫測試是組織向敏捷轉型的踏腳石。道路漫長,但值得去做。我將在本文介紹符合要求的小技巧,以及在開發周期里進行單元測試的步驟。

            有效的單元測試默認要能自動化。沒有自動化,生產力就會下降。沒有自動化,單元測試的習慣也不會持續太久。依靠手工測試(由測試人員或開發人員完成)并不能持續太長時間;在有壓力的情況下,沒人會記得去運行所有的測試,或者去覆蓋所有的場景。自動化是我們的朋友,所有的單元測試框架都支持自動化,而且集成了其他自動化系統。

            單元測試對現代開發來說至關重要

            有代碼相關的測試,我們就有一個天然的安全保障。我們修改的代碼要是帶來了什么問題,測試會告訴我們。這個安全保障越健全,我們對代碼正常運行的信心就越大,對按需修改代碼的能力也就越有信心。

            和其他類型的測試相比,單元測試的主要優點是反饋迅速。在幾秒鐘內運行數百個成套的測試,這對開發流程很有幫助。我們會形成“添加一些代碼,添加測試,測試運行通過,前進”的節奏。小步前進、確保一切正常也意味著調試時間會大大減少。測試能提高生產力也就不足為奇了——在Bug上少花時間,把更多的時間用到新功能的推出上。

            依賴關系的壁壘

            給新建項目添加測試相當容易——畢竟代碼不會阻礙測試。不過這種情況絕對不常見。大多數人都是在處理遺留代碼,這些代碼不太容易測試,有時候甚至運行不起來——它需要的數據或配置可能只存在于生產服務器上。我們或許要為不同的場景創建不同的設置,這也許會花費過多的精力。在很多情況下,我們可能還會為了測試修改代碼。這讓人無法理解:我們編寫測試就是為了能有修改代碼的信心,還沒有測試又該如何去穩妥地修改代碼呢?

            代碼可測性是語言和工具的功能。大家認為Ruby等動態語言是可測的。對于測試的內部代碼,我們可以改變其依賴關系的行為,而不用修改生產代碼。C#或Java等靜態類型語言則不太容易去測試。

            下面有個例子:一個C#的過期檢查方法,檢查是否超過了特定日期:

          public class ExpirationChecker
          {
              private readonly DateTime expirationDate = new DateTime(2012, 1, 1);

              public bool IsExpired()
              {

                  if (DateTime.Now > expirationDate)
                  {
                      return true;
                  }
                  return false;
              }
          }

            在這個例子里,IsExpired方法的DateTime屬性對測試運行時間有強依賴。Now返回的是實際時間。這個方法有兩種情況,它會根據日期返回不同的值。修改計算機時間是絕對不行的,因為我們要在任何時候到任何計算機上去測試場景,并且不能帶來任何副作用。

            要測試到兩種情況,一種可能的解決方案是修改代碼。比如說,我們可以把代碼修改成:

          public bool IsExpired(DateTime now)
          {
              if (now > expirationDate)
              {
                  return true;
              }
              return false;
          }

            這樣,測試可以注入不同、可控的DateTime值,而不用在生產代碼里寫定一個值。我們要是不能修改代碼,可以利用Typemock Isolator等Mocking框架,模擬靜態屬性和方法。針對先前的代碼,測試可以寫成:

          [TestMethod]
          public void IsExpired_BeforeExpirationDate_ReturnFalse()
          {
              Isolate.WhenCalled(() => DateTime.Now)
                  .WillReturn(new DateTime(2000, 1, 1));

              ExpirationChecker checker = new ExpirationChecker();
              var result = checker.IsExpired();

              Assert.IsFalse(result);
          }

            現有的遺留代碼不能輕易修改,因為我們沒有針對它的測試。開始測試遺留代碼之后,我們就能明白:代碼越丑陋,測試越困難。工具可以減輕一些痛苦,但我們要努力去構建安全的環境。

            依賴關系并不是唯一的內容……

            我們很快會遇到的另一個問題是測試維護:測試和被測試代碼耦合在一起。有耦合關系,修改生產代碼就有可能破壞測試。要是代碼修改引起測試失敗,我們就需要回去解決這些問題。很多開發人員害怕維護兩個代碼庫,這種恐懼甚至會讓他們干脆不進行單元測試。真正的維護工作既取決于工具,也取決于技巧。

            編寫好的測試是通過實踐獲得的技能。編寫的測試越多,我們就越精于此,同時會提升測試質量,維護也越來越少。有了測試,我們就有機會重構代碼,這反過來又會讓測試更簡潔、更易讀、更健壯。

            工具對實踐的難易程度有極大的影響。在基礎層,我們需要一個測試框架和一個Mocking框架。在.Net領域,兩種框架的選擇都很豐富。

            編寫第一個測試的準則

            開始的時候,我們通常會試用不同的工具,來理解他們的工作原理。我們往往不會在實際的工作代碼上開始編寫測試。但很快就要給代碼編寫真正的測試。有一些小提示屆時會有用:

            ● 從哪里開始:一般來說,我們編寫測試是針對工作代碼的,無論代碼是Bug修復還是新功能。對Bug修復來說,編寫的測試要檢查修復。對功能來說,測試應檢查正確的行為。

            ● 支架:以我們掌握的知識來看,明智的做法是先添加能確保當前實現運行的測試。添加新的代碼之前先寫測試,因為我們希望在修改現有代碼之前,能有安全的保障。這些測試被稱為“特征測試”,這個術語來自Michael Feathers編寫的《修改代碼的藝術》。

            ● 命名:測試最重要的屬性是它的名字。我們一般不會去看運行通過的測試。但當它失敗時,我們看的就是它的名字。所以挑一個好名字,描述出場景和代碼的預期結果。好名字還有助于我們定位測試里的Bug。

            ● 評審:為了增加測試成功通過的機會,編寫第一個測試時我們應該和同事結對。兩個人都能從實踐中學習,而且我們還能立即評審測試。最好對所測的內容、測試的名稱達成共識,因為這會成為團隊其他人員的基本模板。

            ● AAA:現代測試的結構符合AAA模式——Arrange(測試設置)、Act(調用測試里的代碼)、Assert(測試通過的標準)。如果我們使用測試驅動開發(TDD),我們要先編寫完整的測試,然后再添加代碼。對遺留代碼來說,我們可能需要換一種方式。一旦我們有一個場景和名稱需要測試,那先編寫Act和Assert部分。我們要不停構建Arrange部分,因為對需要準備或仿造的依賴關系,我們知道的要更多一些。然后繼續這么做,直到有一個測試能夠通過。

            ● 重構:一旦準備好了測試,我們就可以重構代碼了。重構和測試都是后天獲得的技能。我們不僅要重構被測試代碼,也要重構測試本身。但DRY(不要重復自己)原則不適用于測試。測試失敗時,我們希望盡快修復問題,所有的測試代碼最好在一個地方,而不是分散在不同的文件里。

            ● 可讀性:測試應該是可讀的,最好是人類可讀。和搭檔評審測試代碼,看他能否理解測試的目的。評審其他測試,看看它們的名稱和內容怎樣與相鄰的測試區分開來。一旦測試失敗,就需要修復它們,最好還是在運行失敗之前評審它們。

            ● 組織:一旦我們有了更多的測試,組織就有了用武之地。測試可以在很多方面有所不同,但最明顯的一個就是如何快速運行。有些測試可能在毫秒內運行完,而有些則需要數秒或好幾分鐘。和工作一樣,我們都希望得到最快的反饋。這就是前面談到的怎么按一定的節奏去進行。要做到這一點,你應該把測試劃分一下,把快的測試和慢的測試分開運行。這能手工(努力)去做,但在.NET領域,Typemock Isolator有一個運行器,能自動按運行速度分離。

            總結

            邁出單元測試的第一步是很有挑戰的。體驗依賴的東西很多——語言、工具、現有代碼、依賴關系和技能。只要稍稍思考,進行大量訓練和實踐,你就能漸入測試的佳境。













          posted on 2012-07-30 09:59 順其自然EVO 閱讀(198) 評論(0)  編輯  收藏 所屬分類: 測試學習專欄

          <2012年7月>
          24252627282930
          1234567
          891011121314
          15161718192021
          22232425262728
          2930311234

          導航

          統計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 湖南省| 阳原县| 巴里| 济阳县| 江华| 南汇区| 泸水县| 宁化县| 华蓥市| 犍为县| 白水县| 抚松县| 襄垣县| 读书| 萨迦县| 旬邑县| 眉山市| 蒙阴县| 盐津县| 鱼台县| 确山县| 东源县| 龙井市| 湟中县| 揭东县| 萍乡市| 酒泉市| 稻城县| 辽宁省| 惠来县| 宁河县| 怀远县| 邛崃市| 蛟河市| 镇安县| 赞皇县| 山西省| 全椒县| 天长市| 祁门县| 进贤县|