跳轉到內容

XQuery/XUnit 註解

來自華夏公益教科書,開放的書籍,為開放的世界

您希望有一種簡單的方法,可以為模組的每個函式新增單元測試,而無需為每個函式建立單獨的測試檔案。

我們將使用 XQuery 3.0 註解為我們的每個函式新增斷言。請注意,以下示例基於 eXist 2.0 版本,並且依賴於 XQuery 3.0 函式。

工作原理

[編輯 | 編輯原始碼]

假設您有一個簡單的 XQuery 函式,它返回字串“Hello World!”,如下所示

declare function myfunct:hello() as xs:string {
    'Hello World!'
};

您希望使用 XQuery 3.0 註解向該函式新增一個斷言。XQuery 3.0 工作草案指出,註解總是以“%”符號開頭,並出現在關鍵字“declare”之後和關鍵字“function”之前,如下所示

declare %test:assertEquals('Hello World!') function myfunct:hello() as xs:string {
    'Hello World!'
};

請注意,%test:assertEquals('Hello World!') 已新增到原始函式中。“斷言”只是一個必須返回 true 才能透過測試的函式。該函式的輸出將自動與輸入字串進行比較,在本例中,assertEquals 函式在字串完全匹配時返回 true。

請注意,此語法與 Java 等其他語言不同,Java 將斷言放在每個方法之前的註釋中。

現在您已經擁有足夠的函式資訊來執行簡單的單元測試。

如何呼叫測試

[編輯 | 編輯原始碼]

要呼叫測試,請使用以下測試套件函式

   test:suite($function* as function) as node()*

該函式將返回一個 XUnit 測試結果檔案。

如果您想測試模組中的所有函式,可以使用 util:list-functions($module-uri) 函式,該函式返回模組的函式物件的序列。

示例測試驅動程式

xquery version "3.0";

(: the following line must be added to each of the modules that include unit tests :)
import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";

(: import the module that contains your test-annotated functions :)
import module namespace mt="http://exist-db.org/xquery/test/my-module" at "mymodule.xqm";

(: the test:suite() function will run all the test-annotated functions in the module whose namespace URI you provide :)
test:suite(util:list-functions("http://exist-db.org/xquery/test/my-module"))

XQuery 註解

[編輯 | 編輯原始碼]

註解是新增到每個函式的結構。它們不會被 XQuery 編譯器編譯,但可以被其他 XQuery 指令碼用來建立一個實際測試檔案的資料庫,這些檔案將被執行。這將測試直接放置在每個 XQuery 函式的上下文中。您可以使用 util:inspect-function() 函式來獲取函式中所有註解的列表。

XUnit 測試結果格式

[編輯 | 編輯原始碼]

XUnit 是賦予一類通用測試框架的家族名稱,這些框架在軟體開發人員中廣為人知。該名稱源於 JUnit,它是第一個廣為人知的框架。

XUnit 還包含一個標準化的 XML 測試結果輸出格式,該格式被許多持續整合工具(如 Jenkins、Hudson、CruiseControl、Team City 等)使用。

以下是一個顯示透過測試的 XUnit 示例結果

<testcase name="prefix:function-name Test #1" classname="my-module" time=".01"/

測試名稱是描述測試的任何字串。您可以新增函式名稱以幫助您瞭解正在測試的函式。您還可以新增 classname 屬性將測試結果分組到測試結果報告中。time 元素是單元測試的執行時間。

失敗測試指示如下

<testcase name="prefix:function-name Test #2" classname="my-module" time=".01">
    <failure  message="prefix:function-name Test #2 has failed" type="failure-code-type-1"/>
</testcase>

有關這些檔案的示例 XML 架構,請參閱 https://gist.github.com/959290

註解示例

[編輯 | 編輯原始碼]

以下是包含單個函式的模組。該函式不接受輸入引數,始終返回字串“a”。

xquery version "3.0";

module namespace mymodule="http://exist-db.org/xquery/test/myfunctions";

import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";

declare %test:assertEquals("123") function mymodule:a() as xs:string {
    '123'
};

然後,我們可以編寫一個測試驅動程式,使用斷言測試此函式

xquery version "3.0";
 
(: the following line must be added to each of the modules that include unit tests :)
import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
 
(: import the module that contains your test-annotated functions :)
import module namespace myfunct="http://exist-db.org/xquery/test/myfunctions" at "mymodule.xqm";
 
(: the test:suite() function will run all the test-annotated functions in the module whose namespace URI you provide :)
test:suite(util:list-functions("http://exist-db.org/xquery/test/myfunctions"))

以下註解示例展示瞭如何向一個簡單的函式新增斷言,該函式將兩個小數相加。請注意,您必須將百分號放置在 declare 和 function 之間。

xquery version "3.0";

import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";

declare %test:assertEquals(70) function test:a1() {
    20 + 50
};

此示例將返回成功的測試。

declare %test:assertEquals(70) function test:a2() {
    21 + 50
};

此示例將返回測試失敗記錄。

斷言是任何在測試透過時返回 true,在測試失敗時返回 false 的語句。斷言構成了 XQuery 註解的基礎。

簡單斷言

[編輯 | 編輯原始碼]

許多 XQuery 測試可以基於單行程式碼執行,而無需測試的先決條件或後置條件。

例如 %test:assertEquals("Heinz Roland Uschi Verona")

檢查函式返回的結果是否與提供的資料相等。如果函式返回的字串序列包含多個專案,則會在專案之間新增空格(如上所示)。

斷言可以接受任何文字序列,包括字串和數字,但不包括 XML 片段。但是,assertEquals 會檢查函式的返回值型別。如果它返回了 XML 片段,它將被規範化(忽略的空格將被剝離)。註解字串也將被解析為 XML,並將使用 deep-equals() 比較這兩個值。因此,如果您的函式返回 XML,則向 %test:assertEquals 提供一個字串,它也會被解析為 XML。

%test:assertEmpty - 如果結果為空字串,則返回 true。

%test:assertExists - 如果結果存在,則返回 true。

%test:assertTrue - 如果結果為 true,則返回 true

%test:assertFalse - 如果結果為 false,則返回 true

%test:assertError("error code") - 預計該函式會失敗並出現錯誤。如果給出了錯誤程式碼(可選),則該錯誤程式碼應包含在錯誤訊息中,否則測試將失敗。

%test:assertXPath("count($result) = 8") - 這是最強大的斷言。它根據任意 XPath 表示式檢查結果。如果斷言為 true

  1. XPath 的結果為布林值 true,或者
  2. XPath 返回非空序列

被呼叫函式的輸出始終包含在變數 $result 中。

將引數傳遞給斷言

[編輯 | 編輯原始碼]

以下示例使用 test:args 註解將輸入引數傳遞給單元測試。

declare
   %test:args('"fenny snake"', "all")
   %test:assertXPath("count($result[@class='scene']) = 2")
   %test:assertXPath("$result/table/tr/td[@class='hi'] = 'fenny'")
function t:show-hits($queryStr as xs:string?, $mode as xs:string) {
   let $result := shakes:do-query($queryStr, $mode)
   return
       shakes:show-hits((), (), $result)
};

在本例中,第一個引數是 $queryStr="fenny snake",第二個引數是 $mode="all"。然後,此示例有兩個單獨的斷言。第一個斷言必須至少具有兩個類為 scene 的結果,而第二個斷言則顯示結果必須包含一個包含行的表,以及一個類為“hi”的表格資料元素。

透過這種方式,可以使用這兩個引數建立許多斷言。

使用多個引數

[編輯 | 編輯原始碼]

您可以根據需要使用 %test:args() 函式多次,為單個函式建立多個測試。您只需重複使用 %test:args() 函式,並在每個 %test:args() 之後新增一組單獨的斷言語句。以下模式

declare
   (: test 1 :)
   %test:args('a')
   %test:assert()
   %test:assert()

   (: test 2 :)
   %test:args('b')
   %test:assert()
   %test:assert()

   (: test 3 :)
   %test:args('b')
   %test:assert()
   %test:assert()

   function prefix:myfunction($in as xs:string) as xs:string {
   ...
   };

輔助註釋

[編輯 | 編輯原始碼]

除了基本的斷言之外,還可以使用一些額外的註釋。您可以使用以下內容來描述測試

%test:name("description")

可用於為測試提供額外的描述。這將用於 xUnit <test> 元素中的 @name 屬性。

還有一些特殊函式,它們將在評估模組中的任何其他測試之前和之後被呼叫。使用它來載入文件、配置索引等。

%test:setUp

%test:tearDown

參考資料

[編輯 | 編輯原始碼]
華夏公益教科書