YUI的測試編寫有效的JavaScript單元測試

2009年1月5日,10:38上午由Nicholas C. Zakas | 開發 | 6評論

其中的-“的雷達在JavaScript開發的動作下在2008年最大的是,單元測試的興趣重現。 YUI的測試 ,YUI的單元測試框架,達成在二月遺傳狀態和其他庫或者介紹他們自己的單元測試框架或開始宣傳現有的。 因此,有很多關於建立為JavaScript單元測試文件。 簡單的JavaScript單元測試是不足夠,但如果你的測試被寫入不當,它們能導致很多失去的時間。 學習編寫有效的JavaScript單元測試會為您節省時間和頭痛,在未來。

您正在測試什麼?

編寫有效的單元測試的關鍵是要了解守信用“單位。” 在測試方面,一個單位是一個孤立的部分代碼,可以獨立於其他部分的代碼測試。 在一個像JavaScript的面向對象的語言,每一種方法被認為是一個單位。 正確的面向對象的設計通常需要很好地封裝為一個單一的目的,因此容易測試的方法。

傳統的單元測試的目的是測試接口實現的,因此,私有方法沒有得到明確的測試。 這就是所謂的黑盒測試。 我們的想法是,你可以換出接口實現和單元測試將仍然通過,因為他們是完全無關的底層實現。 所有的測試知道的是一組必須滿足的約束,他們並不關心如何滿足這些約束。

編寫測試

正如我在我的談話中說,單元測試應該測試輸入和輸出。 輸入可以被命名方法參數或在全球範圍內訪問變量的變化,該方法取決於正常。 輸出可以是返回值,狀態變量的變化,甚至拋出的錯誤。 對於每一個輸入輸出設置,應該有一個單一的單元測試。 每個測試都應該明確說明,“這些投入,我希望這些產出。” 任何偏離這一聲明是一個失敗的測試。

每個測試應盡可能和測試只有一個輸入輸出設置簡單,組合成一個單一的測試集最小化的單元測試的有效性。 例如,考慮下面的測試,稱為函數trim()

 VAR的TestCase =新YAHOO.tool.TestCase({
    名稱:“TRIM()測試”,
     testTrim:函數(){
         RESULT1 = TRIM(“世界您好”);
         YAHOO.util.Assert.areEqual(“世界您好”,RESULT1,“前導空格應該被剝奪。”);
         RESULT2 = TRIM(“世界您好”);
         YAHOO.util.Assert.areEqual(“你好世界”,RESULT2,“尾隨空格應該被剝奪。”);
     }
 }); 

在這裡,測試用例的testTrim()方法實際上是測試兩個不同的輸入輸出集:

  1. 輸入字符串的前導空格;返回值沒有前導空白。
  2. 輸入字符串結尾的空白;返回值沒有尾隨空格。

問題是,這兩套字面上彼此沒有關係,但如果第一組輸入輸出不能產生正確的結果,第二組將永遠不會被測試。 這是其中一個失敗口罩另一個情況。 這是更有效地分離出這些輸入輸出集分為兩個測試:

  VAR的TestCase =新YAHOO.tool.TestCase({
    名稱:“TRIM()測試”,
     testTrimWithLeadingWhiteSpace:函數(){
        結果= TRIM(“世界您好”);
         YAHOO.util.Assert.areEqual(“你好世界”,結果,“前導空格應該被剝奪。”);
     },
     testTrimWithTrailingWhiteSpace:函數(){
        結果= TRIM(“世界您好”);
         YAHOO.util.Assert.areEqual(“世界您好”,結果,“尾隨空格應該被剝奪。”);
     }
 }); 

此代碼現在可以正確地測試trim()函數的輸入輸出集,使它們分開。

總是寫單元測試,如果被測試的代碼可以正常工作。 良好的軟件設計包括制訂這些輸入輸出設置為提前讓你知道在各種情況下的結果應該是什麼。 在這種方式,單元測試,成為除了實際的代碼中的技術要求文件類型。

有效的斷言

編寫單元測試的最重要部分之一,是正確的斷言定義。 每個斷言指定一個條件,如果不符合要求,指示,功能不恰當的行為。 重要的是要使用盡可能多的說法,正確的測試代碼的輸出。 太多的斷言可導致假故障,而過少可導致假通行證。

在前面的例子中,每個測試包含一個單一的斷言,因為這是所有的需要。 我確切地知道,要返回,所以我專門測試值。 測試可能看起來很簡單,但他們把工作做好。 再次,沒有規則的斷言,使一個很好的測試數量,只是確保你測試每一個代碼的預期給定的輸入輸出。

為了使測試失敗更加連貫,你應該包括與每一個斷言失敗的消息。 在YUI的測試,這始終是最後一個參數的任何斷言方法。 失敗消息應該告訴你什麼,不應該發生什麼情況。 一些例子:

 / /錯誤失敗的消息
 YAHOO.util.Assert.areEqual(“你好世界”,結果,“其結果是不是”世界你好“”);

 / /好失敗的消息
 YAHOO.util.Assert.areEqual(“你好世界”,結果,“前導空格應該被剝奪。”);

注意壞事,也是好事失敗消息之間的差別:不好告訴你發生了什麼,並告訴你所預期的。 運行測試時,失敗已經表明,發生了一些意料之外的,所以沒有必要簡單地重複,發生了一些意料之外的。 它更有助於了解應該發生什麼,因為它是您的要求精確地表示。 通過採取這種辦法,故障最終被一個沒有兌現的要求,你可以回去和評估列表。

使用DOM

JavaScript是獨一無二的,因為它經常有環境,DOM關係的其他語言。 很大程度上與DOM交互的方法,單元測試是很難的,因為整個環境必須按順序設置的方法完全執行。 更加複雜的問題是由用戶操作,如鼠標點擊觸發JavaScript的傾向。 YUI的測試提供事件模擬,以幫助創建測試方法是依賴於DOM的互動,但是,這開始越境進入功能測試區。

反對單元測試,功能測試,旨在測試產品用戶的體驗,而不是代碼的輸入輸出集。 如果你發現自己想測試的用戶界面,在一種特定的方式,由於用戶交互響應,那麼你真的要來寫,而不是一些單元測試,功能測試。 YUI的測試可以用來寫一些基本的功能測試,但這樣的測試工具(相當不錯)最流行 ​​的是

確定的東西,如果是一個單元測試的最佳途徑是問如果可以前寫的代碼,它的設計測試確實存在。 單元測試,測試驅動開發的一部分,實際上是應該被寫入實際的代碼,作為一種方法來指導發展的努力。 另一方面,功能測試,不存在提前因為他們綁到用戶界面和如何響應用戶交互更改。

構建測試層次結構

YUI的測試,就像其他單元測試框架,支持層次的測試用例和測試套件。 每個測試套件可以包含其他的測試套件,以及測試用例;只有測試用例可以包含實際測試(與單詞“test”開始的方法)。 最好的方式來組織測試層次結構是遵循一個非常簡單的模式:

  • 你要測試的每個對象創建一個測試套件。
  • 每一個對象,你要測試,並把它添加到對象的測試套件的方法創建一個測試用例。
  • 創建一個為每個輸入輸出設置在每個測試用例的測試。

在這種方式,您的測試層次反映你正在測試的代碼,它很容易找出其中應建立新的考驗。

運行測試!

也許最重要的部分單元測試是經常運行測試。 時定期進行的測試是唯一有效的。 至少,你應該運行單元測試之前檢查源代碼控制的變化。 優化,你還定期運行自動測試,以驗證任何更改後,他們一直致力於源頭控制。 這是您將如何得到單元測試最大的好處是:快速鑑別力,並希望預防回歸。

進一步的信息

共享和擴展: 書籤del.icio.us Digg它! | reddit!

6評論

  1. 銳的自動運行定期的測試? 如果是這樣,你可以分享你如何做到這一點的任何信息? 我特別感興趣的是你如何處理與產卵和關閉用於在測試運行過程中自動構建瀏覽器的處理,以及如何收集和匯總,為構建測試結果。

    西蒙- 1月5日,2009

  2. 我已經看到了thusfar最好的解決辦法是使用Selenium測試瀏覽器的生命週期管理。 你可以將它設置為定期運行,然後監視結果的頁面。

    Nicholas C. Zakas - 1月7日,2009

  3. 首先,感謝寫這美妙的博客條目。 我想以下評論:

    在一個像JavaScript的面向對象的語言,每一種方法被認為是一個單位

    個人比較喜歡一類作為一個單位的思想。 這個類提供了我的一些行為,我想通過我的測試,來解釋行為。 在方法級別,我們似乎是執行在某種意義上鑽。

    單元測試,測試輸入和輸出

    我寧願如果你的資格你狀態的單元測試,單元測試的風格。 有一個不同的學校,相信在互動為基礎的測試單元測試。 如果沒有返回值,狀態變量的變化,或拋出錯誤的輸出。 其重點是與它的合作者(方法調用)的相互作用。

    在YUI的試驗,失敗的消息始終是最後一個參數的任何斷言方法

    他們似乎已經打破的xUnit公約。 xUnit的失敗的消息始終是第一個參數。

    你聲稱:

    //Bad failure message
    YAHOO.util.Assert.areEqual("Hello world", result, "The result wasn't 'Hello world'");

    / /好失敗的消息
    YAHOO.util.Assert.areEqual(“你好世界”,結果,“前導空格應該被剝奪。”);

    雖然我同意你的第二個例子是,比第一次更好,但我認為失敗的消息在第二種情況下是多餘的。 如果你看看下面整個的測試方法,

    testTrimWithLeadingWhiteSpace: function(){
    var result = trim(" Hello world");
    YAHOO.util.Assert.areEqual("Hello world", result, "Leading white space should be stripped.");
    }

    方法的名字已經告訴你,前導空格應修剪或剝離出來。 在這種特定的情況下,我不會放棄任何失敗消息。 如果給貴公司的最佳實踐之一,那麼我寧願喜歡你的失敗消息“前導空格不被剝奪”

    另一方面,功能測試,不存在提前因為他們綁到用戶界面和如何響應用戶交互更改。

    這並非總是如此。 在很多情況下,實際上你可以寫你的功能測試或驗收測試之前創建的任何代碼。 參照驗收測試驅動開發

    你要測試的每個對象創建一個測試套件

    為什麼這很重要嗎? 我不知道這將如何幫助?

    每一個對象,你要測試,並把它添加到對象的測試套件的方法創建一個測試用例。

    我寧願在我的生產代碼中創建測試類的每個重要的實體和行為的類滿足每寫一個測試方法。 有時,我可能有超過1測試的一種方法,有時沒有。 我不知道如果我想創建一個指引,說每一個方法都應該有一個測試。

    評論由納雷什耆那教 - ,2009年1月11日

  4. 您好納雷什

    感謝您有見地的意見。 事實上,有許多學校周圍的測試方法的思想,你對其中一些感動。 我只是想澄清幾個點。

    使斷言失敗消息應發生的聲明是不是多餘的,這是剛剛發生了什麼解釋。 測試的名稱和失敗的消息可能是相似的,在這種情況下,因為測試是如此簡單,但是,他們可能是非常不同的,如果有一些在測試中的斷言。

    我建議設立測試套件和測試用例是讓人們開始。 我不是說每一個方法都應該有一個測試,我說的每一個方法,應該有一個測試用例包含的試驗,探索每個輸入輸出設置,你有。 這一點是有一個邏輯結構,地圖直接向你編程的對象。

    我希望這個澄清一些點;感謝您的意見。

    Nicholas C. Zakas - ,2009年1月12日

  5. 您好尼古拉斯
    我最近開始探索現有的門戶網站上yuitest JS代碼進行單元測試,我們繼承的支持和已經使用了一些YUI庫2。 你已經提出了博客的所有視頻和感謝。
    1。 有例子/ yuitest樣品更實際/現實的情況下使用不同的背景,廣泛的DOM usgae等yuitest網站上的例子一樣,是非常簡單的。
    2。 我之所以問是因為網頁上的內容一般是動態的,是基於對一些先決條件......容器元素是相同的,但內容的變化。 到了每個場景的不同htmls存根是最好嗎?
    3。 有可能是大量使用DOM的一些腳本。 應該像原來的HTML整個HTML在這種情況下,被掐滅。

    評論kaanta - 12月8日,2010

  6. 您好Kaanta

    目前所有的例子,我可以分享文檔中包含的YUI下載(ZIP文件中,檢查測試目錄)的一部分。

    如果你在頁面上測試了很多的互動,然後我通常會建議到實際的頁面加載YUI的測試和運行測試。 這樣,您不必擔心存根出某件東西的工作頁面。

    Nicholas C. Zakas - 12月8日,2010

很抱歉,評論已被封閉,在這個時候。

主辦雅虎

©2006-2012雅虎公司所有權利保留。 隱私政策 - 服務條款

支持WordPress的關於雅虎 虛擬主機