乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      讓開發(fā)自動(dòng)化: 持續(xù)測(cè)試 (轉(zhuǎn)與 developerWorks 中國(guó))

       快樂學(xué)習(xí) 2007-06-15

      2007 年 3 月 26 日

      準(zhǔn)備好開始在您的開發(fā)人員測(cè)試活動(dòng)中大獲全勝嗎?在本期的 讓開發(fā)自動(dòng)化 中,開發(fā)自動(dòng)化專家 Paul Duvall 介紹了幾種自動(dòng)化的開發(fā)人員測(cè)試,每一次改變?cè)创a都能夠運(yùn)行這些測(cè)試。Paul 提供了 Selenium、DbUnit 和 JUnitPerf 測(cè)試的例子,即,如果經(jīng)常 運(yùn)行這些測(cè)試可以幫助您盡早發(fā)現(xiàn)應(yīng)用程序的問題。

      在像 Eclipse 那樣的 IDE 中或者比如在 Ant 構(gòu)建腳本中運(yùn)行單元測(cè)試是確保應(yīng)用程序質(zhì)量的一個(gè)很好的開始;然而,版本控制庫(kù)(如 Subversion)中的源代碼一改變,在單獨(dú)無變動(dòng)的構(gòu)建機(jī)上運(yùn)行單元測(cè)試就有助于檢驗(yàn)開發(fā)生命周期中的問題。而且,運(yùn)行各種類型的開發(fā)人員測(cè)試,如組件測(cè)試、功能測(cè)試和性能測(cè)試,能夠在開發(fā)生命周期中更早地 將問題顯示出來。

      關(guān)于本系列
      作為開發(fā)人員,我們的工作就是為終端用戶實(shí)現(xiàn)過程自動(dòng)化;然而,很多開發(fā)人員卻忽略了將自己的開發(fā)過程自動(dòng)化的機(jī)會(huì)。為此,我編寫了 讓開發(fā)自動(dòng)化 這個(gè)系列的文章,專門探討軟件開發(fā)過程自動(dòng)化的實(shí)際應(yīng)用,并教您何時(shí) 以及如何 成功地應(yīng)用自動(dòng)化。

      通常在持續(xù)集成(CI)環(huán)境中運(yùn)行的開發(fā)人員測(cè)試有效地扮演著代碼質(zhì)量聚光燈的角色。這是因?yàn)槿绻苡行У鼐帉戇@些測(cè)試,則幾乎能夠在問題(如缺陷)產(chǎn)生之時(shí)就將其發(fā)現(xiàn)。不經(jīng)常運(yùn)行測(cè)試通常就不怎么有效,因?yàn)閺漠a(chǎn)生缺陷到發(fā)現(xiàn)該缺陷的時(shí)間相隔很長(zhǎng),但持續(xù)地(即,每一次代碼改變時(shí))運(yùn)行測(cè)試能確??焖侔l(fā)現(xiàn)無意識(shí)的行為。

      本文涵蓋下列內(nèi)容:

      • 通過 Ant 運(yùn)行 JUnit 測(cè)試
      • 使用 JUnit 和 DbUnit 執(zhí)行更長(zhǎng)時(shí)間的運(yùn)行組件測(cè)試
      • 使用 JUnitPerf 確定哪些方法花費(fèi)時(shí)間過久而執(zhí)行失敗
      • 用 Selenium 運(yùn)行基于 Web 的功能測(cè)試
      • 用 Cobertura 訪問代碼覆蓋率
      • 用 CruiseControl 進(jìn)行持續(xù)測(cè)試

      我提供一個(gè)關(guān)于不同類型開發(fā)人員測(cè)試的概覽,和一些可以添加到構(gòu)建過程并使用 Continuous Integration 系統(tǒng)持續(xù)運(yùn)行的例子。

      按 JUnit 進(jìn)行單元測(cè)試

      有人稱之為單元測(cè)試,有人稱之為組件測(cè)試
      經(jīng)常提到的單元測(cè)試其實(shí)更像是一個(gè)組件級(jí)別的測(cè)試。組件測(cè)試通常驗(yàn)證不止一個(gè)類,并且依賴于一些東西,如數(shù)據(jù)庫(kù)或其他重量級(jí)系統(tǒng),如文件系統(tǒng)。但是更重要的是,在測(cè)試的基礎(chǔ)上進(jìn)行測(cè)試,組件測(cè)試比單元測(cè)試運(yùn)行時(shí)間更長(zhǎng)。

      有時(shí),我聽到開發(fā)人員將開發(fā)人員測(cè)試這一術(shù)語(yǔ)與簡(jiǎn)單的單元測(cè)試相混淆;然而,我發(fā)現(xiàn)將單元測(cè)試這一術(shù)語(yǔ)提練得更加明確很有幫助。對(duì)我來說,單元測(cè)試是快速運(yùn)行的 測(cè)試,通常測(cè)試沒有大的外部依賴項(xiàng)(如數(shù)據(jù)庫(kù))的單獨(dú)的類。例如,清單 1 定義了一個(gè)單元測(cè)試,該測(cè)試使用 JUnit 來驗(yàn)證一個(gè)叫做 BeerDaoStub 的存根數(shù)據(jù)類。針對(duì)并未真正連接到數(shù)據(jù)庫(kù)的接口的測(cè)試技術(shù)是一種驗(yàn)證業(yè)務(wù)功能的方法,使用該方法不會(huì)導(dǎo)致花費(fèi)昂貴的設(shè)置成本。另外,這樣做可使測(cè)試保持為一個(gè)真正的單元測(cè)試。


      清單 1. 一個(gè)簡(jiǎn)單的單元測(cè)試
      public void setUp() {
                              beerService = new BeerDaoStub();
                              }
                              public void testUnitGetBeer() {
                              Collection beers = beerService.findAll();
                              assertTrue(beers != null && beers.size() > 0);
                              }
                              

      一旦編寫了一些單元測(cè)試,就可以一直通過 IDE 運(yùn)行這些測(cè)試,但您也想要將這些測(cè)試作為構(gòu)建過程的一部分來運(yùn)行。確保該測(cè)試通過構(gòu)建過程成功運(yùn)行意味著也能從 CI 構(gòu)建的上下文中啟動(dòng)這些相同的測(cè)試。

      清單 2 是一個(gè) Ant 腳本片段,介紹了執(zhí)行一批單元測(cè)試的 junit 任務(wù)。這項(xiàng)任務(wù)與 JUnit 一起運(yùn)作,其妙處在于:定義過的所有測(cè)試現(xiàn)在都能自動(dòng)運(yùn)行并且如果其中任何一個(gè)測(cè)試失敗,則構(gòu)建也將失敗 —— 通過使用 haltonfailure 屬性實(shí)現(xiàn)。


      清單 2. 在 Ant 中運(yùn)行單元測(cè)試
      <junit fork="yes" haltonfailure="true" dir="${basedir}" printsummary="yes">
                              <classpath refid="test.class.path" />
                              <classpath refid="project.class.path"/>
                              <formatter type="plain" usefile="true" />
                              <formatter type="xml" usefile="true" />
                              <batchtest fork="yes" todir="${logs.junit.dir}">
                              <fileset dir="${test.unit.dir}">
                              <patternset refid="test.sources.pattern"/>
                              </fileset>
                              </batchtest>
                              </junit>
                              

      注意:test.unit.dir 指定測(cè)試的位置。這是將這些測(cè)試(在本例中為單元測(cè)試)和其他測(cè)試隔離起來的有效方法。通過利用這項(xiàng)技術(shù),可以通過定義另外的 Ant 目標(biāo)來先運(yùn)行較快的測(cè)試,接著運(yùn)行較慢的測(cè)試(如組件測(cè)試、功能測(cè)試和系統(tǒng)測(cè)試)。





      回頁(yè)首


      集合組件測(cè)試

      由于單元測(cè)試執(zhí)行得相當(dāng)快,很容易將它們作為構(gòu)建的一部分經(jīng)常運(yùn)行。但這些測(cè)試并未達(dá)到一個(gè)高的代碼覆蓋率 —— 其隔離的本質(zhì)決定了它們只測(cè)試一部分功能。編寫具有更多代碼(從而可實(shí)現(xiàn)更多功能)的測(cè)試通常要以附屬框架的形式執(zhí)行更多的調(diào)查工作。一旦開始使用這些幫助框架來編寫測(cè)試,這些測(cè)試就開始成為更高級(jí)別的測(cè)試,我把它們歸類為組件測(cè)試。

      組件測(cè)試是基本的測(cè)試,這些測(cè)試將驗(yàn)證不止一個(gè)類,且通常依賴于外部依賴項(xiàng),如數(shù)據(jù)庫(kù)。組件測(cè)試的編寫方式和單元測(cè)試大體一致,只是前者并非通過模擬或存根類來強(qiáng)制隔離,實(shí)現(xiàn)這些測(cè)試可謂勉為其難,但可以利用框架來便利對(duì)外部依賴項(xiàng)的使用。例如,我通常使用 DbUnit 框架來幫助管理數(shù)據(jù)庫(kù),以便組件測(cè)試可驗(yàn)證依賴數(shù)據(jù)庫(kù)數(shù)據(jù)的代碼功能。

      用 DbUnit 控制數(shù)據(jù)庫(kù)狀態(tài)

      DbUnit 是一個(gè)框架,它使針對(duì)數(shù)據(jù)庫(kù)的測(cè)試過程變得更加簡(jiǎn)單。它提供了一個(gè)標(biāo)準(zhǔn) XML 格式,用于定義一些測(cè)試數(shù)據(jù),以便從數(shù)據(jù)庫(kù)中選擇、更新、插入和刪除數(shù)據(jù)。請(qǐng)牢記,DbUnit 并沒有替換數(shù)據(jù)庫(kù);它只是提供了一種更加有效的機(jī)制來處理測(cè)試數(shù)據(jù)。您可以用 DbUnit 來編寫依賴于特定數(shù)據(jù)的測(cè)試,DbUnit 保證該數(shù)據(jù)位于底層的數(shù)據(jù)庫(kù)中。

      可以在 JUnit 中可編程地使用 DbUnit,或者可以將它作為構(gòu)建過程的一部分使用。該框架帶有一個(gè) Ant 任務(wù),該任務(wù)提供了一種使用 XML 文件來操作、導(dǎo)出或比較數(shù)據(jù)庫(kù)中數(shù)據(jù)的方法。例如,清單 3 演示了 dbunit 任務(wù),在本文的例子中,該任務(wù)將測(cè)試數(shù)據(jù)插入到目標(biāo)數(shù)據(jù)庫(kù)中,然后在運(yùn)行完所有組件測(cè)試后刪除數(shù)據(jù):


      清單 3. 在 Ant 中運(yùn)行組件測(cè)試
      <target name="component-tests">
                              <mkdir dir="${logs.junit.dir}" />
                              <taskdef name="dbunit"
                              classname="org.dbunit.ant.DbUnitTask"/>
                              <dbunit driver="com.mysql.jdbc.Driver"
                              url="jdbc:mysql://localhost:3306/brewery"
                              userid="${db.username.system}"
                              classpathref="db.lib.path"
                              password="${db.password.system}">
                              <operation type="INSERT"
                              src="seedFile.xml"/>
                              </dbunit>
                              <junit fork="yes" haltonfailure="false"
                              failureproperty="tests.failed"
                              haltonerror="true" dir="${basedir}"
                              printsummary="yes">
                              <classpath refid="test.class.path" />
                              <classpath refid="project.class.path"/>
                              <formatter type="plain" usefile="true" />
                              <formatter type="xml" usefile="true" />
                              <batchtest fork="yes" todir="${logs.junit.dir}">
                              <fileset dir="${test.component.dir}">
                              <patternset refid="test.sources.pattern"/>
                              </fileset>
                              </batchtest>
                              </junit>
                              <mkdir dir="${reports.junit.dir}" />
                              <junitreport todir="${reports.junit.dir}">
                              <fileset dir="${logs.junit.dir}">
                              <include name="TEST-*.xml" />
                              <include name="TEST-*.txt" />
                              </fileset>
                              <report format="frames" todir="${reports.junit.dir}" />
                              </junitreport>
                              <dbunit driver="com.mysql.jdbc.Driver"
                              url="jdbc:mysql://localhost:3306/brewery"
                              classpathref="db.lib.path"
                              userid="${db.username.system}"
                              password="${db.password.system}">
                              <operation type="DELETE"
                              src="seedFile.xml"/>
                              </dbunit>
                              </target>
                              

      正如清單 3 所示,現(xiàn)在組件測(cè)試可在執(zhí)行期間依賴駐留在數(shù)據(jù)庫(kù)中的特定數(shù)據(jù)。另外,由于在所有測(cè)試成功執(zhí)行后刪除了所有的數(shù)據(jù),因而此過程現(xiàn)在可重復(fù)執(zhí)行。

      在數(shù)據(jù)庫(kù)中播種

      可以將 dbunit 任務(wù)的 INSERTDELETE 操作類型和一個(gè)種子文件起使用,該文件包含表示數(shù)據(jù)庫(kù)表和相關(guān)行的 XML 元素。例如,清單 4 是清單 3 中引用的 seedFile.xml 文件的內(nèi)容。每個(gè) BEER 元素表示一個(gè)也叫 BEER 的數(shù)據(jù)庫(kù)表,BEER 元素的每個(gè)屬性和其值都映射至相應(yīng)的數(shù)據(jù)庫(kù)列名稱和值。


      清單 4. DbUnit 種子文件
      <?xml version=‘1.0‘ encoding=‘UTF-8‘?>
                              <dataset>
                              <BEER id=‘6‘
                              beer_name=‘Guinness Extra Stout‘
                              brewer=‘St.James Brewery‘
                              date_received=‘2007-02-01‘ />
                              <BEER id=‘7‘
                              beer_name=‘Smuttynose Robust Porter‘
                              brewer=‘Smuttynose Brewery‘
                              date_received=‘2007-02-01‘ />
                              <BEER id=‘8‘
                              beer_name=‘Wolavers pale ale‘
                              brewer=‘Wolaver Brewery‘
                              date_received=‘2007-02-01‘ />
                              </dataset>
                              

      您也許已經(jīng)從清單 3 中注意到,可以在不同的操作中重用 DbUnit 的種子文件。在本文的例子中,將在運(yùn)行組件測(cè)試前使用清單 4 中的文件在數(shù)據(jù)庫(kù)中播種,然后使用相同的文件指示測(cè)試完成時(shí)從數(shù)據(jù)庫(kù)中刪除哪些數(shù)據(jù)。





      回頁(yè)首


      參與性能測(cè)試

      開發(fā)人員完成編碼后,常常要經(jīng)過很長(zhǎng)時(shí)間才執(zhí)行性能測(cè)試,而事實(shí)通常是可以在開發(fā)周期中更早的時(shí)候發(fā)現(xiàn)(并且解決)性能問題。幸運(yùn)地是,有一種方法可解決此問題:持續(xù)測(cè)試或更具體地、持續(xù)地運(yùn)行 JUnitPerf 測(cè)試。

      對(duì)性能測(cè)試來說 JUnitPerf 是完美的

      JUnitPerf 是一個(gè)同 JUnit 協(xié)調(diào)工作的框架,該框架在一個(gè)預(yù)定的時(shí)間限制內(nèi)執(zhí)行測(cè)試用例:如果一個(gè)測(cè)試中的方法所用的時(shí)間比預(yù)期的閾值長(zhǎng),則認(rèn)為該測(cè)試是失敗的。通過將性能測(cè)試集成到自動(dòng)化構(gòu)建中,您能有效地監(jiān)控應(yīng)用程序的性能甚至能在出現(xiàn)性能問題時(shí)使構(gòu)建失敗。

      但我傾向于將 JUnitPerf 用作一種發(fā)現(xiàn)早期性能問題的簡(jiǎn)單方法,而不是將其作為一種機(jī)制來衡量執(zhí)行時(shí)間;像 profilers 這樣的工具更善于提供此類衡量。在本質(zhì)上,可以認(rèn)為 JUnitPerf 是一個(gè)早期的警告系統(tǒng)。

      在清單 5 中,我定義了一個(gè) JUnit 測(cè)試,該測(cè)試使用 JUnitPef 來驗(yàn)證 BeerServicePerformanceTest 測(cè)試類中的 testLongRunningMethod 測(cè)試的執(zhí)行時(shí)間。如果執(zhí)行該測(cè)試方法所花的時(shí)間多于 1000 毫秒,則測(cè)試失敗。


      清單 5. 使用 JUnitPerf 的基于性能的測(cè)試
      package com.beer.business.service;
                              import com.clarkware.junitperf.*;
                              import junit.framework.Test;
                              public class ExampleTimedTest {
                              public static Test suite() {
                              long maxElapsedTime = 1000;
                              Test testCase = new BeerServicePerformanceTest("testLongRunningMethod");
                              Test timedTest = new TimedTest(testCase, maxElapsedTime);
                              return timedTest;
                              }
                              public static void main(String[] args) {
                              junit.textui.TestRunner.run(suite());
                              }
                              }
                              

      使用精確計(jì)時(shí)作為方法執(zhí)行時(shí)間的標(biāo)準(zhǔn)時(shí)要小心;測(cè)試的建立和銷毀時(shí)間包含在整個(gè)執(zhí)行時(shí)間中。此外,在早期的性能測(cè)試中,精確測(cè)定執(zhí)行速度在更大程度上是一門藝術(shù)而不是科學(xué)。





      回頁(yè)首


      使用 Selenium 進(jìn)行功能測(cè)試

      可隨意編寫所有需要的單元測(cè)試和組件測(cè)試,但如果要編寫一個(gè)提供某種類型的用戶界面的應(yīng)用程序(例如 Web 應(yīng)用程序),則需要測(cè)試表示層。以 Web 應(yīng)用程序?yàn)槔枰?yàn)證用戶場(chǎng)景的導(dǎo)航,另外還要驗(yàn)證場(chǎng)景的功能是正常的。盡管如此,直到最近,這類測(cè)試都常被證明是一個(gè)負(fù)擔(dān),需要購(gòu)買工具來促進(jìn)開發(fā)周期晚期的測(cè)試。此外,這些工具幾乎不能適合構(gòu)建過程,即使測(cè)試構(gòu)建得足夠早也是如此。

      深入 Selenium

      但近幾年來,一些著眼于功能測(cè)試的開放源碼工具脫穎而出;而且,能輕易地在開發(fā)生命周期的早期使用這些工具。工具如 Selenium 和 Watir 都是開放源碼的;另外,它們構(gòu)建時(shí)考慮到了開發(fā)人員。除了用各種語(yǔ)言(例如 Java 編程和 Python)編程定義 Selenium 測(cè)試之外,Selenium 也提供了一種易于學(xué)習(xí)的表格驅(qū)動(dòng)格式,此格式也能被非技術(shù)類型使用。

      Selenium 框架使用 JavaScript 來執(zhí)行基于 Web 的接受測(cè)試,該測(cè)試打開一個(gè)瀏覽器并運(yùn)行表格驅(qū)動(dòng)測(cè)試。例如,清單 6 展示了一個(gè)表示簡(jiǎn)單的 Selenium 測(cè)試的 HTML 表。該測(cè)試的多個(gè)步驟打開一個(gè) Web 應(yīng)用程序,然后使用有效的用戶名和密碼執(zhí)行登錄。測(cè)試結(jié)果生成到一個(gè) HTML 表中,在 Selenium 運(yùn)行完所有的測(cè)試后,能查看該表。


      清單 6. 使用 Selenium 的功能測(cè)試
      <html>
                              <head>
                              <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
                              <title>MyTest</title>
                              </head>
                              <body>
                              <table cellpadding="1" cellspacing="1" border="1">
                              <thead>
                              </thead><tbody>
                              <tr>
                              <td>open</td>
                              <td>/beer/</td>
                              <td></td>
                              </tr>
                              <tr>
                              <td>type</td>
                              <td>username</td>
                              <td>admin</td>
                              </tr>
                              <tr>
                              <td>type</td>
                              <td>password</td>
                              <td>password</td>
                              </tr>
                              <tr>
                              <td>clickAndWait</td>
                              <td>//input[@value=‘Login‘]</td>
                              <td></td>
                              </tr>
                              <tr>
                              <td>verifyTextPresent</td>
                              <td>Logged in as admin</td>
                              <td></td>
                              </tr>
                              </tbody></table>
                              </body>
                              </html>
                              

      使用清單 6 中基于表格的格式,可以定義多個(gè)接受測(cè)試。也可以將測(cè)試分組成,一次執(zhí)行一整套測(cè)試。

      使用 Ant 驅(qū)動(dòng) Selenium

      Selenium 的偉大之處在于它是在考慮了 CI 的基礎(chǔ)上從頭創(chuàng)建的,因?yàn)槟隳茉谙?Ant 那樣的構(gòu)建工具中運(yùn)行 Selenium。此外,由于框架設(shè)計(jì)者的高瞻遠(yuǎn)矚,如果任何 Selenium 接受測(cè)試失敗,您也可以讓整個(gè)構(gòu)建失敗。例如,清單 7 展示了一個(gè) Ant 任務(wù),該任務(wù)使用 Selenium 遠(yuǎn)程控制服務(wù)器在一個(gè) Web 應(yīng)用程序中執(zhí)行一系列表格驅(qū)動(dòng)測(cè)試:


      清單 7. 使用 Ant 運(yùn)行 Selenium
      <?xml version="1.0" encoding="iso-8859-1"?>
                              <project name="functional-tests" default="run-selenium-tests" basedir=".">
                              <property file="${basedir}/selenium.properties"/>
                              <import file="${basedir}/common-environment.xml"/>
                              <property name="acceptance.test.lib.dir"
                              value="${functional.test.dir}" />
                              <property name="firefox" value="*firefox" />
                              <property name="base.url"
                              value="http://${web.host.name}:${web.port}" />
                              <property name="acceptance.test.list.dir"
                              value="${functional.test.dir}" />
                              <property name="acceptance.test.report.dir"
                              value="${functional.test.dir}" />
                              <target name="run-selenium-tests">
                              <mkdir dir="${reports.dir}" />
                              <java jar="${acceptance.test.lib.dir}/selenium-server.jar"
                              fork="true">
                              <arg line="-htmlSuite "${firefox}""/>
                              <arg line=""${base.url}""/>
                              <arg line=""${acceptance.test.list.dir}/${test.suite}""/>
                              <arg line=""${reports.dir}/index.html""/>
                              <arg line="-timeout ${timeout}"/>
                              </java>
                              </target>
                              <target name="stop-server">
                              <get taskname="selenium-shutdown"
                              src="http://${web.host.name}:
                              ${selenium.rc.port}/selenium-server/driver/?cmd=shutDown"
                              dest="result.txt" ignoreerrors="true" />
                              <echo taskname="selenium-shutdown"
                              message="Errors during shutdown are expected" />
                              </target>
                              </project>
                              

      執(zhí)行 Selenium 測(cè)試時(shí),當(dāng)框架打開 Web 瀏覽器、閃電般執(zhí)行測(cè)試,然后關(guān)閉該瀏覽器并生成 HTML 報(bào)告時(shí),不要被嚇到。這是一種在開發(fā)生命周期的早期更快更容易地發(fā)現(xiàn)問題的方法(此時(shí)它們更易處理)。





      回頁(yè)首


      使用 Cobertura 報(bào)告代碼覆蓋率

      是否達(dá)到 100% 就是問題所在
      運(yùn)行像 Cobertura 或者 Emma 這樣的工具時(shí),記住以下方面很重要:在一個(gè)特殊的方法中實(shí)現(xiàn) 100% 的行覆蓋并不意味著該方法沒有缺陷或者它已被完全測(cè)試。例如,如果您編寫了一個(gè)針對(duì) if 語(yǔ)句的測(cè)試,該測(cè)試包含邏輯 And,而測(cè)試針對(duì)的是表達(dá)式的左側(cè)部分,則像 Cobertura 這樣的工具將報(bào)告 100% 行覆蓋,但是實(shí)際上,您僅執(zhí)行了該語(yǔ)句的 50%;因此僅完成了 50% 的分支覆蓋。

      現(xiàn)在已經(jīng)編寫了一些測(cè)試,如何確定所有這些測(cè)試執(zhí)行什么 呢?幸運(yùn)的是,此問題可由像 Cobertura 這樣的代碼覆蓋工具來解答。代碼覆蓋工具可報(bào)告測(cè)試覆蓋率 —— 以行覆蓋或分支覆蓋形式表示 —— 它表示測(cè)試運(yùn)行時(shí)所涉及的代碼量。

      清單 8 展示了一個(gè) Ant 腳本。該腳本使用 Cobertura 生成一份關(guān)于代碼覆蓋率的 HTML 報(bào)告,代碼覆蓋率通過運(yùn)行一系列 JUnit 測(cè)試獲得:


      清單 8. 使用 Ant 和 Cobertura 報(bào)告代碼覆蓋率
      <target name="instrument-classes">
                              <mkdir dir="${instrumented.dir}" />
                              <delete file="cobertura.ser" />
                              <cobertura-instrument todir="${instrumented.dir}">
                              <ignore regex="org.apache.log4j.*" />
                              <fileset dir="${classes.dir}">
                              <include name="**/*.class" />
                              <exclude name="**/*Test.class" />
                              </fileset>
                              </cobertura-instrument>
                              </target>
                              <target name="run-instrumented-tests" depends="instrument-classes">
                              <mkdir dir="${logs.junit.dir}" />
                              <junit fork="yes" haltonfailure="true" dir="${basedir}" printsummary="yes">
                              <sysproperty key="net.sourceforge.cobertura.datafile" file="cobertura.ser" />
                              <classpath location="${instrumented.dir}" />
                              <classpath location="${classes.dir}" />
                              <classpath refid="test.class.path" />
                              <classpath refid="project.class.path"/>
                              <formatter type="plain" usefile="true" />
                              <formatter type="xml" usefile="true" />
                              <batchtest fork="yes" todir="${logs.junit.dir}">
                              <fileset dir="${test.component.dir}">
                              <patternset refid="test.sources.pattern"/>
                              </fileset>
                              </batchtest>
                              </junit>
                              </target>
                              

      Cobertura 產(chǎn)生了一個(gè)如圖 1 中所示的 HTML 報(bào)告。請(qǐng)注意行覆蓋和分支覆蓋的百分比是以包計(jì)算的。可單擊每一個(gè)包,獲得類級(jí)別的行百分比和路徑百分比,甚至能看到執(zhí)行的源代碼行和它們執(zhí)行的次數(shù)。


      圖 1. 使用 Cobertura 和 Ant 生成 HTML 報(bào)告
       使用 Cobertura 和 Ant 生成 HTML 報(bào)告
      需要多高的代碼覆蓋率?
      理想情況下,您可能想針對(duì)每個(gè)路徑執(zhí)行一次測(cè)試。也就是說,如果整個(gè)代碼基址有 20,000 的循環(huán)復(fù)雜度的話,則需要 20,000 次測(cè)試。我從未遇到過具有 100% 的路徑覆蓋的項(xiàng)目,不過我曾見過具有近 100% 的行覆蓋的團(tuán)隊(duì)。

      已經(jīng)介紹了多種類型的測(cè)試,甚至介紹了如何測(cè)量這些測(cè)試的覆蓋率 —— 但是如何確保以正常的間隔執(zhí)行 這些測(cè)試呢?恰好,這正是 CI 服務(wù)器(如 CruiseControl)大顯身手的地方,接下來對(duì)它進(jìn)行介紹。

      持續(xù)運(yùn)行測(cè)試

      一旦將這些各式各樣的開發(fā)人員測(cè)試類型合并到一個(gè)構(gòu)建過程中時(shí),可以將這些測(cè)試中的一些(或者全部)作為 CI 過程的一部分運(yùn)行。例如,清單 9 是 CruiseControl 的 config.xml 文件的一個(gè)片段,我在其中定義了一些東西。首先,我讓 CruiseControl 每?jī)煞昼姳O(jiān)控一次 Subversion 庫(kù)中的改變。如果發(fā)現(xiàn)任何改變,則 CruiseControl 將啟動(dòng)一個(gè)叫做 build-${project.name}.xml委托 構(gòu)建腳本(通常,此腳本用 Ant 編寫)。該委托構(gòu)建腳本調(diào)用項(xiàng)目的構(gòu)建腳本,后者執(zhí)行編譯并運(yùn)行測(cè)試。

      我也定義了一些邏輯,將所有不同類型的測(cè)試結(jié)果合并到一個(gè) CruiseControl 日志文件中。而且,我還利用 CruiseControl 的功能將不同工具生成的報(bào)告鏈接(使用 artifactspublisher 標(biāo)簽)到 Build Artifacts 鏈接中,Build Artifacts 可以從 CruiseControl 的顯示板應(yīng)用程序中獲得。


      清單 9. 使用 CruiseControl 的 CI
      ...
                              <modificationset quietperiod="30">
                              <svn RepositoryLocation="http:///trunk/brewery"
                              username="bfranklin"
                              password="G0Fly@Kite"/>
                              </modificationset>
                              <schedule interval="120">
                              <ant anthome="apache-ant-1.6.5" buildfile="build-${project.name}.xml"/>
                              </schedule>
                              <log dir="logs/${project.name}">
                              <merge dir="projects/${project.name}/_reports/unit"/>
                              <merge dir="projects/${project.name}/_reports/component"/>
                              <merge dir="projects/${project.name}/_reports/performance"/>
                              <merge dir="projects/${project.name}/_reports/functional"/>
                              <merge dir="projects/${project.name}/_reports/coverage"/>
                              </log>
                              <publishers>
                              <artifactspublisher
                              dir="projects/${project.name}/_reports/"
                              dest="projects/artifacts/${project.name}"/>
                              </publishers>
                              ...
                              

      在將每個(gè)源變更應(yīng)用到版本控制庫(kù)中時(shí),不必運(yùn)行每個(gè)定義的測(cè)試。例如,可以設(shè)置 CI 系統(tǒng)執(zhí)行構(gòu)建(通常稱作提交構(gòu)建),該構(gòu)建只在代碼簽入時(shí)運(yùn)行單元測(cè)試??梢詾樘峤粯?gòu)建補(bǔ)充一些更重量級(jí)的構(gòu)建,例如像運(yùn)行組件測(cè)試、功能測(cè)試、性能測(cè)試以及甚至執(zhí)行代碼檢查的構(gòu)建(請(qǐng)參閱 參考資料)。這些構(gòu)建可以以更低的頻率運(yùn)行(如一天一次)。您也可以選擇在提交構(gòu)建之后立即運(yùn)行這些測(cè)試和檢查。





      回頁(yè)首


      調(diào)用所有測(cè)試

      持續(xù)測(cè)試包括了廣度和頻度。通過授權(quán)執(zhí)行不同類型的測(cè)試,可獲得更大范圍的覆蓋和信任。此外,通過持續(xù)運(yùn)行這些測(cè)試,幾乎能在問題產(chǎn)生就發(fā)現(xiàn)它們。

      僅僅進(jìn)行單元測(cè)試(至少我所定義的單元測(cè)試),并不能使你在項(xiàng)目上走得很遠(yuǎn)。取得更高的代碼覆蓋率并且增加團(tuán)隊(duì)的信心,需要通力合作并執(zhí)行自動(dòng)化組件測(cè)試、性能測(cè)試和功能測(cè)試。此外,使用框架和像 JUnit、Selenium 以及 Cobertura 這樣的工具能輕松實(shí)現(xiàn)構(gòu)建自動(dòng)化,這也意味著在 CI 系統(tǒng)的幫助下,能夠在每次將變更提交到版本控制庫(kù)中時(shí),有效地執(zhí)行測(cè)試套件。這肯定是一種萬無一失的提高平均成功率的方法,您不這么認(rèn)為嗎?



      參考資料

      學(xué)習(xí)
      • 您可以參閱本文在 developerWorks 全球站點(diǎn)上的 英文原文

      • 用 Cobertura 測(cè)量測(cè)試覆蓋率”(Elliotte Rusty Harold,IBM developerWorks,2005 年 5 月):Elliotte Rusty Harold 介紹了如何通過 Cobertura 發(fā)現(xiàn)潛伏著 bug 的未測(cè)試代碼。

      • Effective Unit Testing with DbUnit”(Andrew Glover,OnJava,2004 年 1 月):Andrew Glover 介紹了使用 DbUnit 的數(shù)據(jù)庫(kù)-依賴測(cè)試。

      • 追求代碼質(zhì)量: 用 JUnitPerf 進(jìn)行性能測(cè)試”(Andrew Glover,IBM developerWorks,2006 年 11 月):兩個(gè)監(jiān)控可擴(kuò)縮性和性能的簡(jiǎn)單測(cè)試。

      • 選擇持續(xù)集成服務(wù)器”(Paul Duvall,IBM developerWorks,2006 年 9 月):對(duì)開源 CI 服務(wù)器:CruiseControl、Luntbuild 和 Continuum 的調(diào)查。

      • 通過測(cè)試分類實(shí)現(xiàn)敏捷構(gòu)建(Andrew Glover,IBM developerWorks,2006 年 10 月):Andrew Glover 揭示了三種確保端對(duì)端系統(tǒng)健壯性所需的測(cè)試,然后介紹如何按類別自動(dòng)排序和運(yùn)行測(cè)試。

      • 可重復(fù)的系統(tǒng)測(cè)試 (Andrew Glover,IBM developerWorks,2006 年 9 月):Andrew Glover 介紹了 Cargo,這是一個(gè)以通用方式自動(dòng)化容器管理的開源框架,所以您每次都能編寫邏輯上可重復(fù)的系統(tǒng)測(cè)試。

      • 用 Selenium 自動(dòng)化驗(yàn)收測(cè)試”(Christian Hellsten,IBM developerWorks,2005 年 12 月):如何用 Selenium 測(cè)試工具對(duì) Ruby on Rails 和 Ajax 應(yīng)用程序進(jìn)行功能測(cè)試。

      • Continuous Integration”(Martin Fowler):Fowler 關(guān)于持續(xù)集成實(shí)踐的基本文章。

      • JUnitPerf”(Mike Clark):了解如何將 JUnitPerf 用于早期性能測(cè)試。

      • 讓開發(fā)自動(dòng)化 系列(Paul Duvall,IBM developerWorks):探討實(shí)踐中對(duì)自動(dòng)化軟件開發(fā)過程的使用,了解何時(shí)及如何成功地應(yīng)用自動(dòng)化。

      • 追求代碼質(zhì)量 (Andrew Glover,IBM developerWorks):了解更多有關(guān)代碼規(guī)格、測(cè)試框架和編寫高質(zhì)量代碼的信息。

      • developerWorks Java 技術(shù)專區(qū):數(shù)百篇關(guān)于 Java 編程各方面的文章。


      獲得產(chǎn)品和技術(shù)
      • JUnitPerf:使用 JUnit 訪問性能瓶頸。

      • Cobertura:計(jì)算被測(cè)試訪問過的源代碼的百分比。

      • Selenium:一個(gè)針對(duì) Web 應(yīng)用程序的測(cè)試工具。

      • DbUnit:瞄準(zhǔn)數(shù)據(jù)庫(kù)驅(qū)動(dòng)項(xiàng)目的 JUnit 擴(kuò)展。

      • CruiseControl:用于持續(xù)構(gòu)建過程的框架。

         


      關(guān)于作者

      Paul Duvall 是 Stelligent Incorporated 的 CTO,該公司利用有效的開發(fā)人員測(cè)試策略,以及能夠讓團(tuán)隊(duì)盡早盡多地監(jiān)視和提高代碼質(zhì)量的持續(xù)集成技術(shù),幫助其他企業(yè)解決軟件的質(zhì)量問題。他還是 UML? 2 Toolkit 一書的作者之一,目前正在與他人合作撰寫 Continuous Integration: Improving Software Quality and Reducing Risk (Addison-Wesley) 一書。

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多