| 摘要本文從實(shí)際使用Cucumber這一工具的角度,以Cucumber-JVM實(shí)現(xiàn)為基礎(chǔ),采用了不同的事例闡述了:如何編寫feature文件,如何從feature文件生成對應(yīng)的Steps,如何生成不同格式的報(bào)告,如何定制化的運(yùn)行測試用例以及在與其他主流工具結(jié)合中如何避免報(bào)告失真、如何與主流持續(xù)集成工具結(jié)合使用等,為大家在日常工作中使用Cucumber釋疑解惑。 Cucumber是什么Cucumber是BDD模式下實(shí)現(xiàn)可執(zhí)行規(guī)范(Executable Specifications)的開源工具,但是它的使命并不局限于做自動化驗(yàn)收測試,更加重要的在于其能夠在團(tuán)隊(duì)成員之間構(gòu)建統(tǒng)一的交流基礎(chǔ)(feature文件)、規(guī)范交流用語(Domain Specific Language)、提高各個(gè)利益相關(guān)方(Business Stakeholders)溝通效率和效果從而達(dá)到提升產(chǎn)品質(zhì)量、做成客戶期望得到的產(chǎn)品這一最終目標(biāo)。 如何使用CucumberCucumber有很多種語言的實(shí)現(xiàn)版本,例如Java、Ruby、.NET、JavaScript等等,并且Cucumber可以和主流的測試框架很好地集成,常見的Selenium、SpringFramework、Ruby on Rails等,能夠方便地引入到你的測試工作中去,幾乎沒有任何門檻。以一個(gè)Java測試項(xiàng)目為例,介紹如何使用Cucumber的Java語言實(shí)現(xiàn)版本:Cucumber-JVM。 將Cucumber-JVM依賴加入到項(xiàng)目中如果你的項(xiàng)目是使用Maven管理所依賴的第三方依賴jar包,那么引入Cucumber-JVM將是一件優(yōu)雅而且輕松的事情,只需要簡單的將如下的Code Snippet加入到項(xiàng)目的pom.xml的“dependencies”下即可:     <dependency>
        <groupId>info.cukes</groupId>
        <artifactId>cucumber-java</artifactId>
        <version>${cucumber.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>info.cukes</groupId>
        <artifactId>cucumber-junit</artifactId>
        <version>${cucumber.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
 123456789101112131415161718
 關(guān)于version,請盡量選擇適合自己項(xiàng)目的,如果你使用的是Java 7,如果沒有特殊的要求可以與本文一樣采用<cucumber.version>1.2.2</cucumber.version> 編寫 Executable SpecificationCucumber之所以受到如此的推崇,與其Executable Specification這個(gè)特性不無關(guān)系。顧名思義,可執(zhí)行規(guī)范給出了至少兩個(gè)方面的意義: - 可執(zhí)行性(Executable):你可以像執(zhí)行代碼(Java、Ruby…)一樣運(yùn)行這些規(guī)范,來驗(yàn)證、驗(yàn)收目標(biāo)應(yīng)用。當(dāng)然,這一點(diǎn)是從技術(shù)人員的視角來看的;
 - 規(guī)范性(Specification):從非技術(shù)人員的視角觸發(fā),相比驗(yàn)證本身,他們更加關(guān)心系統(tǒng)功能的清晰描述:系統(tǒng)在什么場景下能夠做什么樣的事情。
 這看似簡單的兩方面似乎關(guān)聯(lián)并不是很大,但是如何能夠在同一個(gè)基礎(chǔ)(feature files)之上做到兩者的融合,卻是Cucumber最大的妙處。從項(xiàng)目管理人員的角度來看,Cucumber是技術(shù)人員和非技術(shù)人員交流的橋梁,從更加深的層面來看,Cucumber能夠使增加各個(gè)利益相關(guān)方的溝通,因?yàn)橹挥猩钊氲臏贤?,在各方都理解了真正期望的功能這一基礎(chǔ)之上,才能產(chǎn)出都認(rèn)可的Executable Specification! 回歸到工具這一層面,Cucumber是以feature文件來組織測試的,相信大家都很清楚這里之所以采用feature這個(gè)后綴,其實(shí)正是為了凸顯用戶在使用系統(tǒng)中所能夠享受到的服務(wù)和功能。以ATM取錢場景為例子,通過如下的大致步驟: - 創(chuàng)建feature文件,
 - 生成測試Step Definitions,
 - 運(yùn)行測試用例。
 來具體說明如何在測試工作中使用Cucumber。 創(chuàng)建Feature文件自動柜員機(jī)(ATM)大家都非常熟悉,現(xiàn)在假設(shè)你在為某一個(gè)銀行所提供的固定金額取款功能編寫測試用例,經(jīng)過跟利益相關(guān)方討論之后,針對這一功能,你們得出了如下的場景定義,此處以Feature文件的形式寫出來: src/main/resources/features/FixedAmountWithdraw.feature
  # language: zh-CN
功能: 使用ATM固定金額方式取款
  通?!叭】睢辈藛伟藥讉€(gè)固定金額,使用這些固定金額取款可以避免從鍵盤輸入提取金額,從而可以加速交易,提高取款的效率。
  場景大綱: 固定金額取款
    假如 我的賬戶中有余額"<accountBalance>"元
    當(dāng) 我選擇固定金額取款方式取出"<withdrawAmount>"元
    那么 我應(yīng)該收到現(xiàn)金"<receivedAmount>"元
    而且 我賬戶的余額應(yīng)該是"<remainingBalance>"元
    例子:
      | accountBalance | withdrawAmount | receivedAmount | remainingBalance |
      | 1000.00        | 100.00         | 100.00         | 900.00           |
      | 500.00         | 500.00         | 500.00         | 0.00             |
 上述文件中,需要說明的是: - 1,
 # language: zh-CN表明feature文件中所使用的描述語言是中文簡體,Cucumber本身支持超過30種語言(此處語言是Spoken Language而非Programming Language)- 通過
 java cucumber.api.cli.Main --i18n help查看所支持的所有Spoken Language;- 通過
 java cucumber.api.cli.Main --i18n LANG查看所給定語言支持的所有關(guān)鍵字,比如查看中文簡體(zh-CN)支持的所有Gherkin關(guān)鍵字,可以通過執(zhí)行命令java cucumber.api.cli.Main --i18n zh-CN查看,其輸出類似如下內(nèi)容:       | feature          | "功能"                   |
      | background       | "背景"                   |
      | scenario         | "場景", "劇本"             |
      | scenario_outline | "場景大綱", "劇本大綱"         |
      | examples         | "例子"                   |
      | given            | "* ", "假如", "假設(shè)", "假定" |
      | when             | "* ", "當(dāng)"              |
      | then             | "* ", "那么"             |
      | and              | "* ", "而且", "并且", "同時(shí)" |
      | but              | "* ", "但是"             |
      | given (code)     | "假如", "假設(shè)", "假定"       |
      | when (code)      | "當(dāng)"                    |
      | then (code)      | "那么"                   |
      | and (code)       | "而且", "并且", "同時(shí)"       |
      | but (code)       | "但是"                   |
 
2,采用中文描述feature文件,首先得益于Cucumber本身的支持,但是另外一個(gè)最重要的原因是我期望所描述的功能可以被利益相關(guān)方清楚地讀懂,使用利益相關(guān)方的Spoken Language來撰寫規(guī)范可以使溝通更加方便,是Cucumber的又一大利器;3,如果不使用# language: zh-CN這個(gè)header,默認(rèn)情況下,Cucumber會以英語解析feature文件尋找場景和Steps,此時(shí),Gherkin的關(guān)鍵詞必須使用英文: Feature: 使用ATM固定金額方式取款
  通?!叭】睢辈藛伟藥讉€(gè)固定金額,使用這些固定金額取款可以避免從鍵盤輸入提取金額,從而可以加速交易,提高取款的效率。
  Scenario Outline: 固定金額取款
    Given 我的賬戶中有余額"<accountBalance>"元
    When 我選擇固定金額取款方式取出"<withdrawAmount>"元
    Then 我應(yīng)該收到現(xiàn)金"<receivedAmount>"元
    And 我賬戶的余額應(yīng)該是"<remainingBalance>"元
    Examples:
      | accountBalance | withdrawAmount | receivedAmount | remainingBalance |
      | 1000.00        | 100.00         | 100.00         | 900.00           |
      | 500.00         | 500.00         | 500.00         | 0.00             |
 alias cucumber='java -classpath ".:./target/classes:/home/admin/.m2/repository/info/cukes/cucumber-java/1.2.2/cucumber-java-1.2.2.jar:/home/admin/.m2/repository/info/cukes/cucumber-core/1.2.2/cucumber-core-1.2.2.jar:/home/admin/.m2/repository/info/cukes/cucumber-jvm-deps/1.0.3/cucumber-jvm-deps-1.0.3.jar:/home/admin/.m2/repository/info/cukes/gherkin/2.12.2/gherkin-2.12.2.jar:/home/admin/.m2/repository/info/cukes/cucumber-junit/1.2.2/cucumber-junit-1.2.2.jar:/home/admin/.m2/repository/junit/junit/4.12/junit-4.12.jar:/home/admin/.m2/repository/info/cukes/cucumber-html/0.2.3/cucumber-html-0.2.3.jar" cucumber.api.cli.Main'
 – 對于Windows,可以在PATH指定的任意一個(gè)目錄(比如 C:\Windows\System32\)下,創(chuàng)建一個(gè)cucumber.bat,其中的內(nèi)容可以參考如下: java -classpath ".;.\target\classes;C;\Users\admin\.m2\repository\info\cukes\cucumber-java\1.2.2\cucumber-java-1.2.2.jar;C;\Users\admin\.m2\repository\info\cukes\cucumber-core\1.2.2\cucumber-core-1.2.2.jar;C;\Users\admin\.m2\repository\info\cukes\cucumber-jvm-deps\1.0.3\cucumber-jvm-deps-1.0.3.jar;C;\Users\admin\.m2\repository\info\cukes\gherkin\2.12.2\gherkin-2.12.2.jar;C;\Users\admin\.m2\repository\info\cukes\cucumber-junit\1.2.2\cucumber-junit-1.2.2.jar;C;\Users\admin\.m2\repository\junit\junit\4.12\junit-4.12.jar;C;\Users\admin\.m2\repository\info\cukes\cucumber-html\0.2.3\cucumber-html-0.2.3.jar" cucumber.api.cli.Main %*
 生成Step definitions當(dāng)然,通過上述的dry run產(chǎn)生的dummy steps definitions可以作為創(chuàng)建對應(yīng)Step definitions的Skeleton,在沒有IDE協(xié)助你生成Step definitions的前提下,我推薦大家使用dry run的輸出作為基礎(chǔ),創(chuàng)建自己的Step definitions。此處,本文推薦大家使用集成開發(fā)環(huán)境所提供的Step definitions工具來實(shí)現(xiàn)上述操作,其原因是: - 1,集成開發(fā)工具能夠生成較為完善的代碼,不僅僅是steps definitions本身,而且包含了對應(yīng)的Java Class類;
 - 2,對于剛接觸Cucumber不久且充滿好奇心的使用者而言,能夠接觸到Cucumber的實(shí)現(xiàn)代碼,更加有利于了解其運(yùn)行原理,而集成開發(fā)環(huán)境在這一點(diǎn)上能夠提供很大的幫助。
 由于Eclipse暫時(shí)并不支持Steps definitions的生成操作,下面本文以JetBrain的優(yōu)秀Java IDE環(huán)境Intellij IDEA述如何實(shí)現(xiàn)從feature生成Steps definitions。 在Intellij IDEA下生成Steps definitions在編寫feature文件的過程中,IDEA會提示目前文件中哪些步驟(steps)是沒有對應(yīng)的Java step definitions,如下圖所示,Intellij IDEA會以黃色的小燈泡這個(gè)提示標(biāo)志來提醒作者: 
  作者只需要按照如下的步驟來生成對應(yīng)的Steps definitions即可: - 1,點(diǎn)擊該提示圖標(biāo),并從彈出的菜單項(xiàng)中選擇“Create Step Definition”或者“Create All Steps Definition”;
 - 2,在彈出的“Create New Step Definition File”模式窗口中填寫文件名稱、實(shí)現(xiàn)語言以及文件位置等信息即可;
 - 3,重復(fù)步驟1和2直到所有的step都生成對應(yīng)的Java step definition即可。
 
      package io.cucumber.samples.dw.steps;
    import cucumber.api.PendingException;
    import cucumber.api.java.zh_cn.*;
    public class FixedAmountWithdrawStepdefs {
        @那么("^我應(yīng)該收到現(xiàn)金\"([^\"]*)\"元$")
        public void 我應(yīng)該收到現(xiàn)金_元(String accountBalance) throws Throwable {
            // Express the Regexp above with the code you wish you had
            throw new PendingException();
        }
        @假如("^我的賬戶中有余額\"([^\"]*)\"元$")
        public void 我的賬戶中有余額_元(String withdrawAmount) throws Throwable {
            // Express the Regexp above with the code you wish you had
            throw new PendingException();
        }
        @當(dāng)("^我選擇固定金額取款方式取出\"([^\"]*)\"元$")
        public void 我選擇固定金額取款方式取出_元(String receivedAmount) throws Throwable {
            // Express the Regexp above with the code you wish you had
            throw new PendingException();
        }
        @而且("^我賬戶的余額應(yīng)該是\"([^\"]*)\"元$")
        public void 我賬戶的余額應(yīng)該是_元(String remainingBalance) throws Throwable {
            // Express the Regexp above with the code you wish you had
            throw new PendingException();
        }
    }
 123456789101112131415161718192021222324252627282930
 在Eclipse下生成Steps definitions在Eclipse中,仍然可以通過Dry run的方式來實(shí)現(xiàn)部分steps definitions code的生成,這一點(diǎn)請參考前文中Cucumber dry run命令。 在IDEA中調(diào)試運(yùn)行測試用例Cucumber測試用例有兩種方式可以啟動,debug mode下,兩種方式都可以對Steps definitions代碼進(jìn)行debug: - 1,Run with JUnit方式;
 這種方式要求必須有JUnit Test來觸發(fā),常規(guī)的做法是:
 - a,創(chuàng)建一個(gè)空白的JUnit Test:所謂空白就是在JUnit Test類中沒有任何方法;
 - b,按照Cucumber所提供的Options annotation來引入所需要使用的feature文件和Steps definitions類;
 - c,按照需要設(shè)置報(bào)告格式等,下面是以JUnit方式觸發(fā)FixedAmountWithdraw.feature文件的配置Sample;
         package io.cucumber.samples.dw;
        import cucumber.api.CucumberOptions;
        import cucumber.api.junit.Cucumber;
        import org.junit.runner.RunWith;
        @RunWith(Cucumber.class)
        @CucumberOptions(
                format = {"json:target/json-report/dw.json"}
                , features = {"classpath:features/FixedAmountWithdraw.feature"}
                , glue = {"io.cucumber.samples.dw.steps"}
        )
        public class AppTest {
        }
 
d,然后就可以以JUnit用例的方式運(yùn)行或者調(diào)試測試用例的實(shí)現(xiàn)了,此處不再贅述。 Eclipse和Intellij IDEA都支持這樣的調(diào)試方式
 
 2,IDEA下直接運(yùn)行Scenario/Scenario Outlinea,IDEA下可以直接選中所需要運(yùn)行或者調(diào)試的Scenario/Scenario Outline;b,然后右鍵打開彈出菜單選擇“Run”或者“Debug”;c,并選擇對應(yīng)的Scenario名稱即可運(yùn)行或者調(diào)試該場景對應(yīng)的Steps definitions。 
 
 定制化運(yùn)行Cucumber測試用例Cucumber是以feature文件來組織測試用例的,關(guān)于如何customized運(yùn)行Cucumber測試用例,首先需要向大家介紹就是如何組織feature文件。Feature文件通常是按照子文件夾來組織的,你可以按照功能歸屬、類別、Sprint Plan等等來做,至于哪一種是最好的,通常需要并沒有準(zhǔn)確的答案,一個(gè)可行的建議是先按照你認(rèn)為最可行的方式組織起來,嘗試一段時(shí)間,持續(xù)一兩個(gè)iteration,后續(xù)如果沒有發(fā)現(xiàn)明顯的問題就可以堅(jiān)持下來。通常你需要嘗試幾次才能確定哪一種方式最適合自己的team。子文件夾就像是一本書的目錄對應(yīng)的章節(jié),便于索引。 Cucumber還支持為Scenario指定標(biāo)簽(tag),tag是以“@”字符開頭的一個(gè)單詞,用來表述被修飾對象(可以是feature,可以是scenario,可以是scenario outline甚至可以是scenario outline下的examples)所屬的類別。Tag可以是一個(gè),也可以是多個(gè),Cucumber本身并不限制tag的個(gè)數(shù)。 # language: zh-CN
@withdraw
功能: 使用ATM固定金額方式取款
  通?!叭】睢辈藛伟藥讉€(gè)固定金額,使用這些固定金額取款可以避免從鍵盤輸入提取金額,
  從而可以加速交易,提高取款的效率。
  @fixedAmount
  場景大綱: 固定金額取款
    假如 我的賬戶中有余額"<accountBalance>"元
    當(dāng) 我選擇固定金額取款方式取出"<withdrawAmount>"元
    那么 我應(yīng)該收到現(xiàn)金"<receivedAmount>"元
    而且 我賬戶的余額應(yīng)該是"<remainingBalance>"元
  @positive
    例子:
      | accountBalance | withdrawAmount | receivedAmount | remainingBalance |
      | 1000.00        | 100.00         | 100.00         | 900.00           |
      | 500.00         | 500.00         | 500.00         | 0.00             |
  @negative
    例子:
      | accountBalance | withdrawAmount | receivedAmount | remainingBalance |
      | 1000.00        | 1100.00        | 0.00           | 1000.00          |
      | 500.00         | 600.00         | 0.00           | 500.00           |
 12345678910111213141516171819202122
 其中 
  | Tag修飾對象 | 表述的意義 |  
  | Feature | 該Feature文件中的所有Scenario和Scenario Outline都會繼承修飾在Feature的tag,比如上例中的@withdraw |  
  | Scenario / Scenario Outline | 表示tag適用于Scenario/Scenario Outline,Scenario Outline下的Examples會繼承定義在其上的tag,比如上例中的@fixedAmount |  
  | Examples | 只對Scenario Outline及當(dāng)前的Examples有效,比如上例中的@positive和@negative |  如果說子文件夾是目錄對應(yīng)的章節(jié),那么tag可以看做是添加到書中的便簽,讓你迅速找到對應(yīng)的scenario。Tag可以看做是另外一種維度的索引,可以為Cucumber測試用例集合添加不同的維度,從而便于檢索和過濾,這一點(diǎn)在選擇性地運(yùn)行Cucumber測試用例時(shí)顯得尤其重要! 如何與持續(xù)集成工具集成主流的持續(xù)集成工具有很多,被廣泛采用的開源工具當(dāng)推Jenkins。Cucumber reporting功能也可以被Jenkins支持,Github中有開源的Jenkins Plugin:Publish pretty cucumber-jvm reports on Jenkins。對于其具體的用法,其文檔中已經(jīng)有詳細(xì)介紹,作者嘗試過可以按照其步驟成功集成Cucumber Reporting功能到Jenkins,此處不再贅述。 結(jié)束語本文從實(shí)際使用Cucumber這一工具的角度,以Cucumber-JVM實(shí)現(xiàn)為基礎(chǔ),采用了不同的事例闡述了:如何編寫feature文件,如何從feature文件生成對應(yīng)的Steps,如何生成不同格式的報(bào)告,如何定制化的運(yùn)行測試用例以及在與其他主流工具結(jié)合中如何避免報(bào)告失真、如何與主流持續(xù)集成工具結(jié)合使用等,為大家在日常工作中使用Cucumber釋疑解惑?;氐紺ucumber作為自動化工具的層面,其在API測試中的使用也有很多可圈可點(diǎn)之處,本系列后續(xù)文章會對此進(jìn)行闡述: - 《活用Cucumber測試服務(wù)端開放API》講述結(jié)合BDD風(fēng)格測試工具Rest-Assured驗(yàn)證API功能,如何使用JSON Schema驗(yàn)證API的返回結(jié)構(gòu)等一系列實(shí)用的經(jīng)驗(yàn)分享。
 下載指定Cucumber運(yùn)行結(jié)果報(bào)告Cucumber本身支持多種報(bào)告格式以適用于不同環(huán)境下調(diào)用的報(bào)告輸出: - pretty :用于在命令行環(huán)境下執(zhí)行Cucumber測試用例所產(chǎn)生的報(bào)告,如果你的console支持,pretty形式的報(bào)告還可以按照顏色顯示不同的運(yùn)行結(jié)果;如下圖所示的例子分別顯示了用例執(zhí)行通過和用例沒有Steps definitions的輸出報(bào)告:
 
  
 
json :多用于在持續(xù)集成環(huán)境下的夸機(jī)器生成報(bào)告時(shí)使用,比如在用例執(zhí)行的機(jī)器A上運(yùn)行Cucumber測試用例,而在調(diào)度或報(bào)告機(jī)器B上生成用例執(zhí)行報(bào)告,此時(shí)只需要把生成的json報(bào)告?zhèn)鬏數(shù)綑C(jī)器B上即可。 
 html :用于生成簡單的HTML格式的報(bào)告以便查看Cucumber測試用例運(yùn)行的結(jié)果 
 junit :用于生成JUnit格式的報(bào)告     <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <testsuite failures="0" name="cucumber.runtime.formatter.JUnitFormatter" skipped="0" tests="1" time="0.177755">
        <testcase classname="使用ATM固定金額方式取款" name="固定金額取款" time="0.177755">
            <system-out>
            <![CDATA[
                假如我的賬戶中有余額"500.00"元.........................................................passed
                當(dāng)我選擇固定金額取款方式取出"500.00"元.....................................................passed
                那么我應(yīng)該收到現(xiàn)金"500.00"元..........................................................passed
                而且我賬戶的余額應(yīng)該是"0.00"元..........................................................passed
            ]]>
            </system-out>
        </testcase>
    </testsuite>
 |