concordion學(xué)習(xí)筆記

concordion簡要

  1. concordion的實(shí)現(xiàn)可以理解為活文檔,自然語言,易讀,包含很多examples。文檔是html,所以構(gòu)建出可以navigable structure的文檔。 頻繁的運(yùn)行可以使文檔和系統(tǒng)功能同步。如果系統(tǒng)行為發(fā)生了變化,相應(yīng)的文檔就會(huì)fail
  2. concordion文檔不包含實(shí)現(xiàn)細(xì)節(jié),把需求和實(shí)現(xiàn)分開,包含列子,實(shí)現(xiàn)方式可變,方便重構(gòu)
  3. concordion是Junit的擴(kuò)展
  4. 易學(xué),基于specifications by example、
  5. 不需要特定的語言格式,使用自然語言,關(guān)注易讀性
  6. 豐富的測試報(bào)告。 HTML格式的報(bào)告可以加入超鏈接和圖片等多種
  7. 文檔可以當(dāng)成unit test來運(yùn)行。具備和Junit測試一樣的深度
  8. BA QA DEV可以一起合作寫出文檔
  9. 文檔可以適用于不同級別 unit, component, subsystem, system
  10. 活文檔可以用來定義story的驗(yàn)收條件,幫助PO來控制項(xiàng)目范圍
  11. 提高測試覆蓋率。不管實(shí)現(xiàn)如何,需求一定會(huì)被滿足

concordion實(shí)現(xiàn)

一個(gè)concordion規(guī)格文檔由兩部分組成

  • 描述功能的html文件

  • 測試代碼 (基于Junit)

兩個(gè)文件應(yīng)該在同一個(gè)文件夾下

為了兩個(gè)文檔的融合使用,html里面應(yīng)該包含concordion的相關(guān)命令。這個(gè)命令以結(jié)點(diǎn)的屬性形式出現(xiàn)

<html xmlns:concordion="http://www.concordion.org/2007/concordion">
     <body>
        <p concordion:assertEquals="getGreeting()">Hello World!</p>
     </body>
</html>

測試代碼基于Junit,方法名需要和html中的名字一樣,使用ConcordionRunner和Junit的RunWith來讓concordion把相關(guān)的命令和類聯(lián)系起來

package example;

import org.concordion.integration.junit4.ConcordionRunner;
import org.junit.runner.RunWith;

@RunWith(ConcordionRunner.class)
public class HelloWorldFixture {

     public String getGreeting() {
        return "Hello World!";
    }
}

測試報(bào)告輸出默認(rèn)目錄是 system property: java.io.tmpdir.

concordion使用方法

concordion:assertEquals:驗(yàn)證執(zhí)行方法的返回值和所期待的結(jié)果一致

concordion:set:定義一個(gè)變量

<p>
The greeting for user <span concordion:set="#firstName">Bob</span>
will be:
<span concordion:assertEquals="greetingFor(#firstName)">Hello Bob!</span>
</p>

concordion:execute:可以執(zhí)行一個(gè)測試代碼里面的方法

一般來說,執(zhí)行一個(gè)沒有返回值的方法,要用set或是setUp開頭。
<p>
If the time is
    <span concordion:set="#time">09:00AM</span>
    <span concordion:execute="setCurrentTime(#time)" />
                then the greeting will say:
    <span concordion:assertEquals="getGreeting()">Good Morning World!</span>
</p>

利用#TEXT這個(gè)特殊變量可以簡寫上面的spec??梢圆皇褂胏oncordion:set。

在把#TEXT作為參數(shù)傳給方法,后面接著#TEXT的值

        <p>
            If the time is
            <span concordion:execute="setCurrentTime(#TEXT)">09:00AM</span>
                 then the greeting will say:
            <span concordion:assertEquals="getGreeting()">Good Morning World!</span>
        </p>
執(zhí)行一個(gè)有返回值的方法,可以使用方法接受返回值
        <p>
            The full name
            <span concordion:execute="#result = split(#TEXT)">John Smith</span>
                will be broken into first name
            <span concordion:assertEquals="#result.firstName">John</span>
                and last name
            <span concordion:assertEquals="#result.lastName">Smith</span>.
            </p>

這里的#result是一個(gè)類,split()方法返回一個(gè)類對象. firstName and lastName 是類屬性

處理不尋常的語句結(jié)構(gòu)

當(dāng)我們面對規(guī)格文檔描述不利于concordion命令的實(shí)現(xiàn)的時(shí)候,我們可以把execute命令放到更上一層結(jié)點(diǎn)結(jié)構(gòu)當(dāng)中

<p concordion:execute="#greeting = greetingFor(#firstName)">
      The greeting "<span concordion:assertEquals="#greeting">Hello Bob!</span>"
      should be given to user <span concordion:set="#firstName">Bob</span>
      when he logs in.
</p>
execute命令的執(zhí)行是有一定的順序的
  1. 首先是處理所有child的set命令
  2. 然后是自己的命令
  3. 然后是自己child的execute命令
  4. 最后是所有child的assertEquals方法
concoirdion:execute在table里面的使用

concordion:execute on a <table> 可以設(shè)置在table heading級別來執(zhí)行table里面的每行數(shù)據(jù)

當(dāng)我們需要表現(xiàn)同一個(gè)行為的不同列子的時(shí)候,需要重復(fù)同樣的語句結(jié)構(gòu)。這種情況下可以使用table來表達(dá)

            <table>
                <tr>
                    <th>Full Name</th>
                    <th>First Name</th>
                    <th>Last Name</th>
                </tr>
                <tr concordion:execute="#result = split(#fullName)">
                    <td concordion:set="#fullName">John Smith</td>
                    <td concordion:assertEquals="#result.firstName">John</td>
                    <td concordion:assertEquals="#result.lastName">Smith</td>
                </tr>
                <tr concordion:execute="#result = split(#fullName)">
                    <td concordion:set="#fullName">David Peterson</td>
                    <td concordion:assertEquals="#result.firstName">David</td>
                    <td concordion:assertEquals="#result.lastName">Peterson</td>
                </tr>
            </table>

但是這樣看起來也是比較重復(fù)的

所以concordion支持把concordion命令放在table heading級別,每一個(gè)td就會(huì)重復(fù)th的命令

下面的列子中,execute在table級別,set, assertEquals在table heading級別

            <table concordion:execute="#result = split(#fullName)">
                <tr>
                    <th concordion:set="#fullName">Full Name</th>
                    <th concordion:assertEquals="#result.firstName">First Name</th>
                    <th concordion:assertEquals="#result.lastName">Last Name</th>
                </tr>
                <tr>
                    <td>John Smith</td>
                    <td>John</td>
                    <td>Smith</td>
                </tr>
                <tr>
                    <td>David Peterson</td>
                    <td>David</td>
                    <td>Peterson</td>
                </tr>
            </table>
concordion:execute在list上的使用

concordion:execute on a <list> 可以給方法傳遞有層級的測試數(shù)據(jù)

當(dāng)我們使用concordion在列表上的時(shí)候,比如ol 和 ul 元素, 該execute命令會(huì)執(zhí)行在每一個(gè)列表元素上。
這個(gè)特性能幫我們設(shè)置有層級結(jié)構(gòu)的測試數(shù)據(jù)

<ol concordion:execute="parseNode(#TEXT, #LEVEL)">
    <li>Europe</li>
    <ul>
        <li>Austria</li>
        <ol>
            <li>Vienna</li>
        </ol>
        <li>UK</li>
        <ul>
            <li>England</li>
            <li>Scotland</li>
        </ul>
        <li>France</li>
    </ul>
    <li>Australia</li>
</ol>

當(dāng)執(zhí)行到<ol concordion:execute="parseNode(#TEXT, #LEVEL)”>時(shí),給parseNode穿的參數(shù)就等于下面的表格

TEXT | #LEVEL

------- | -------
Europe | 1
Austria | 2
Vienna |3
UK | 2
England | 3
Scotland | 3
France | 2
Australia | 1

concordion:verifyRows

可以取出方法返回的可迭代的數(shù)據(jù),并一一取出做驗(yàn)證。

當(dāng)我們需要處理方法返回的大量數(shù)據(jù)內(nèi)容,并且這些數(shù)據(jù)是可迭代的時(shí)候,我們可以使用concordion:verifyRows。 比如處理一個(gè)方法返回值是列表,而我們需要對列表里面的每一個(gè)值進(jìn)行驗(yàn)證

    <table concordion:execute="setUpUser(#username)">
        <tr><th concordion:set="#username">Username</th></tr>
        <tr><td>john.lennon</td></tr>
        <tr><td>ringo.starr</td></tr>
        <tr><td>george.harrison</td></tr>
        <tr><td>paul.mccartney</td></tr>
    </table>

    <p>Searching for "<b concordion:set="#searchString">arr</b>" will return:</p>

    <table concordion:verifyRows="#username : getSearchResultsFor(#searchString)">
        <tr><th concordion:assertEquals="#username">Matching Usernames</th></tr>
        <tr><td>george.harrison</td></tr>
        <tr><td>ringo.starr</td></tr>
    </table>

verifyRows的語法是: var : iteration_object #loopVar : expression

expression是一個(gè)可迭代對象并且對象里面的元素對象順序是已知的

loopVar能夠訪問返回迭代對象的每一個(gè)元素,并且支持使用assertEquals去進(jìn)行驗(yàn)證

從expression返回的對象元素的順序必須與行里設(shè)置的測試數(shù)據(jù)是一致的

注解

當(dāng)我們把未完成的測試代碼加入到我們的build當(dāng)中,為了避免使build失效,我們可以加注解。

  • @ExpectedToPass 期待pass
  • @ExpectedToFail 期待fail
  • @Unimplemented 未完成
For example:

import org.concordion.api.ExpectedToFail;
import org.concordion.integration.junit4.ConcordionRunner;
import org.junit.runner.RunWith;

@ExpectedToFail
@RunWith(ConcordionRunner.class)
public class GreetingTest {

  public String greetingFor(String firstName) {
        return "TODO";
  }
}
Fail-Fast

當(dāng)出現(xiàn)異常的時(shí)候,concordion會(huì)繼續(xù)執(zhí)行剩下的測試來展示所有的問題。使用Fail-Fast可以使concordion遇到第一個(gè)異常時(shí)就停止繼續(xù)執(zhí)行,跳出測試。

方法是使用注解

Fail-Fast有一個(gè)參數(shù)onExceptionType,這個(gè)參數(shù)是一個(gè)列表,列表里面的內(nèi)容是各種異常類型,只有在執(zhí)行當(dāng)中遇到列表里面的異常類型,F(xiàn)ail-Fast才會(huì)生效。

import org.concordion.api.FailFast;
import org.concordion.integration.junit4.ConcordionRunner;
import org.junit.runner.RunWith;

@FailFast(onExceptionType={DatabaseUnavailableException.class, IOException.class})
public class MyDataTest {

   public void connectToDatabase() {
        ....
   }
}
concordion:run 可以使該文檔link到其他文檔,并且run
concordion:assertTrue 驗(yàn)證執(zhí)行方法返回值是不是True
concordion:assertFalse 驗(yàn)證執(zhí)行方法返回值是不是False
<p>
    When user <b concordion:set="#firstName">Bob</b>
    logs in, the greeting will be:
    <b concordion:assertEquals="greetingFor(#firstName)">Hello Bob!</b>
</p>

<p>
    The first name <span concordion:assertTrue="#firstName.startsWith(#letter)">starts
    with <b concordion:set="#letter">B</b></span>.
</p>

<p>
    The first name <span concordion:assertTrue="#firstName.startsWith(#letter)">starts
    with <b concordion:set="#letter">C</b></span>.
</p>
支持Junit4.5以上, 測試代碼使用ConcordionRunner
package example;

import org.concordion.integration.junit4.ConcordionRunner;
import org.junit.runner.RunWith;

@RunWith(ConcordionRunner.class)
public class HelloWorldFixture {

    public String getGreeting() {
        return "Hello World!";
    }
}

返回值的類型 也可以是map

    public Map split(String fullName) {
        String[] words = fullName.split(" ");
        Map<String, String> results = new HashMap<String, String>();
        results.put("firstName", words[0]);
        results.put("lastName", words[1]);
        return results;
    }

可以使用MultiValueResult來返回多個(gè)結(jié)果

MultiValueResult 是concordion api的一個(gè)類

package example;

import org.concordion.api.MultiValueResult;
import org.concordion.integration.junit4.ConcordionRunner;
import org.junit.runner.RunWith;

@RunWith(ConcordionRunner.class)
public class SplittingNamesTest {

    public MultiValueResult split(String fullName) {
        String[] words = fullName.split(" ");
        return multiValueResult()
                .with("firstName", words[0])
                .with("lastName", words[1]);
    }
}

使用這個(gè)方法,我們可以在測試代碼里面設(shè)置比如屬性firstName的值,然后在specification里面直接調(diào)用

<span concordion:assertEquals="#result.firstName">John</span>

特殊變量 #HREF

當(dāng)我們想使用外部文件作為測試數(shù)據(jù)時(shí),可以使用#HREF指向一個(gè)link,這個(gè)link就是數(shù)據(jù)源文件。

比如我們把測試數(shù)據(jù)放在一個(gè)csv文件里面,我們就可以在html中寫一個(gè)link,然后在這個(gè)<a>中使用concordion命令和#HREF

<a href="blah.csv" concordion:execute="#x = myMethod(#HREF)">test file</a>

<span concordion:assertEquals="#x">blah.csv</span>
Write specifications, not scripts

只在Specification中描述feature和功能,不要有太詳細(xì)的步驟,具體的實(shí)現(xiàn)細(xì)節(jié)都應(yīng)該放在測試代碼里面。

Specification可以說是high level的測試腳本

concordion:echo is able to print the variable to test report
  <div class="example">

<h3>Example</h3>

      <p>
          When user <b concordion:set="#firstName">World</b>
          logs in, the greeting will be:
          <b concordion:assertEquals="greetingFor(#firstName)">Hello, World!</b>
      </p>

      <p>
          Username: <span concordion:echo="#firstName" />  # this is the variable created in specification file
      </p>

  </div>

  <div class="example">

<h3>Example</h3>

      <p>
          When user <b concordion:set="#firstName">World</b>
          logs in, the greeting will be:
          <b concordion:assertEquals="greetingFor(#firstName)">Hello, World!</b>
      </p>

      <p>
          Username: <span concordion:echo="username" /> # this is the class variable from test fixture code
      </p>

  </div>
@RunWith(ConcordionRunner.class)
public class HelloWorldFixture {

    public String username = "username";

    public String greetingFor(String firstName) {
        return new Greeter().greetingFor(firstName);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,680評論 19 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,811評論 18 399
  • 轉(zhuǎn)載,覺得這篇寫 SQLAlchemy Core,寫得非常不錯(cuò)。不過后續(xù)他沒寫SQLAlchemy ORM... ...
    非夢nj閱讀 5,605評論 1 14
  • 一、源題QUESTION 1The instance abnormally terminates because ...
    貓貓_tomluo閱讀 1,755評論 0 2
  • 瀑布流布局是寬度固定,高度隨機(jī),從上到下的布局方式 父元素相對定位(relative),子元素絕對定位(absol...
    嘿菠蘿閱讀 1,622評論 0 0

友情鏈接更多精彩內(nèi)容