NO IMAGE

轉自http://www.ibm.com/developerworks/cn/opensource/os-cn-soapui/

 

REST 作為一種針對 Web 應用的全新架構風格,近年變得越來越流行。soapUI 是一個對 Web 服務進行測試的開源工具, 它可以讓開發人員直觀的測試基於 SOAP 的 Web 服務, 隨著新版本的推出,soapUI 也增加了對 REST 的支援。本文主要介紹如何利用 soapUI 對 REST 服務進行測試

劉 欣, 高階軟體工程師, EMC

劉欣是一位 IBM CDL 的軟體工程師,目前從事企業電子商務應用的開發,他對敏捷開發、自動化的單元測試和整合測試、Ruby/RoR、Open source 有濃厚的興趣。

張 麗婷, 軟體工程師, EMC

張麗婷是一位 IBM CDL 的軟體工程師, 目前從事企業電子商務應用開發,她對模型驅動架構,SOA 有濃厚的興趣。

2009 年 12 月 31 日

REST 服務介紹

REST(Representational State Transfer)是 Roy Fielding 博士在 2000 年提出的一種新的軟體架構風格,它以資源(resource)為核心,使用 HTTP、 URI、XML 以及 HTML 等現有流行協議和標準來完成對資源的操作及顯示。 這些操作包括獲取、建立、修改和刪除資源,分別對應於 HTTP 協議的 GET、POST、PUT 和 DELETE 方法。REST 架構定義了以下設計準則:

  • 網路中的所有事物都被抽象為資源(resource)。
  • 每個資源對應一個唯一的資源標識(resource identifier)。
  • 通過通用的聯結器介面(generic connector interface)對資源進行操作。
  • 對資源的各種操作不會改變資源標識。
  • 所有的操作都是無狀態的(stateless)。

REST 服務(RESTful Service)是一種基於 HTTP 和 REST 準則的輕量級 Web 服務。這類服務可以看作一系列資源(resource)的集合,服務的定義可以視為以下三個切面的組合 :

  • 訪問 Web Service 的 URI,如:http://example.com/resources。
  • Web Service 所支援的資料 MIME 型別,如:JSON, XML, YAML 等。
  • Web Service 使用 HTTP 協議支援的操作,如 GET, POST, PUT, DELETE。

相比目前流行的 Web 服務實現方案 SOAP 和 XML-RPC, REST 服務更加簡潔,它可以完全通過 HTTP 協議實現,還可以利用快取 Cache 來提高響應速度, 其效能,效率和易用性等方面均優於 SOAP 協議。 本文主要介紹如何使用 soapUI 來測試此類 Web 服務。


soapUI 介紹

由於 Web 服務是被程式呼叫的, 一般不會提供介面讓終端使用者或測試人員直接使用,在 soapUI 等工具出現之前,測試人員不得不自己編寫程式來測試它, 這就要求測試人員花費很大的精力瞭解底層的介面,呼叫關係和詳細的協議,導致他們不能把注意力集中到測試中。

soapUI 的出現極大的改變了這一局面。 作為一個開源的工具,soapUI 強大的功能、易用的介面,吸引了很多使用者。使用者可以在 soapUI 中通過簡單的操作完成複雜的測試,不需要了解底層的細節, 極大的減輕了工作量。soapUI 支援多樣的測試, 例如功能測試,效能測試,迴歸測試等。到目前為止 soapUI 的下載量已經超過了 100 萬次,成為了事實的 Web 服務測試標準和領先的 Web 服務測試工具。 它不僅僅可以測試基於 SOAP 的 Web 服務,也可以測試 REST 風格的 Web 服務,後者也是本文介紹的重點。

soapUI 基於 Java 開發,支援多個平臺,安裝非常簡單。讀者可以到 soapUI 的 官方網站下載一個安裝包 ( 本文使用的是 Window 版本 3.0.1),直接安裝即可。在該安裝包中,包括了一個 soapUI 所需要的 JRE1.6 版本。安裝完畢以後,讀者需要設定 JAVA_HOME 變數指向到相應的 JRE 目錄,同時修改 PATH 變數,將 JRE1.6 的 bin 目錄新增進去。


REST 服務案例

為了避免空洞的講解,同時為了更好的展示 soapUI 對 REST 服務的測試功能,本文假想了一個線上書店 (http://www.example.com) 的例子。該線上書店對外提供了一些 REST 服務讓第三方的應用程式呼叫。 為了讓讀者把注意力集中在使用 soapUI 進行測試上,我們對這些 REST 服務進行了必要的簡化,僅僅只包含下面 3 種功能:書籍列表,書籍詳情和新增評論。這 3 個 REST 服務覆蓋了層次狀的 REST 資源、基本的 HTTP 操作和多種展現形式。

服務名稱HTTP 操作資源 URI資源展現註釋
書籍列表GEThttp://www.example.com/booksapplication/json,text/xml該 REST 服務的目的是列出線上書店中的書籍列表,參見
程式碼清單 1

程式碼清單 2
書籍詳情GEThttp://www.example.com/books/<book id >application/json該 REST 服務目的是給定一個書籍 ID,返回該書籍的詳細資訊,參見
程式碼清單 3
,需要注意的是書籍詳細資訊是一個帶有層次結構的 json 展示
新增評論POSThttp://www.example.com/books/<book id >/comments無 , 系統僅僅返回 200 OK該 REST 服務的目的是對一個書籍新增評論,呼叫方需要 POST 類似 author=xxx&content=xxx 的資料到伺服器端。
清單 1. 書籍列表 application/json
 {"books": [ 
{"book":    { 
"id": "1234", 
"name": "book1", 
"price": 29 
}}, 
{"book":    { 
"id": "5678", 
"name": "book2", 
"price": 18 
}} 
]}
清單 2. 書籍列表 text/xml
 <bookes> 
<book> 
<id>1234</id> 
<name>book1</name> 
<price>29.0</price> 
</book> 
<book> 
<id>5678</id> 
<name>book2</name> 
<price>18</price> 
</book> 
</bookes>
清單 3. 書籍詳情 application/json
 { 
"id": "1234", 
"name": "book1", 
"description": "this is book 1", 
"author": "author1", 
"price": 29, 
"comments":    [ 
{"comment":       { 
"user": "user1", 
"content": "good book"
}}, 
{"comment":       { 
"user": "user2", 
"content": "not bad"
}} 
] 
}

在 soapUI 中建立測試用例

基本概念

在建立測試用例之前,我們先來看一看在 soapUI 中的基本概念,soapUI 把 REST 服務、資源及其操作組織為一個層次結構。如
圖 1
所示,主要包括如下層次:

  • 專案定義:位於最上層 (BookStoreTest),專案可以包含多個服務的定義。
  • REST 服務定義:服務其實是對多個 REST 資源的一個分組,在我們的例子中只有一個服務 BookStoreServie
  • REST 資源定義:具體描述該資源的名稱,URI, 引數等屬性
  • REST 方法定義:針對每個資源的方法 (GET,POST,PUT,DELETE 等 ),圖 1 中的方法名就是 GetBookList
  • REST 操作請求定義:基於每個方法,可以有一個或多個請求操作,如 GetBookListRequest,這些請求操作才是真正被 soapUI 所呼叫執行的。每個請求可以設定非常豐富的資訊,例如 Accept 型別,請求的 Header 資訊,執行了該請求以後,就能以各種方式檢視執行結果。但是這裡還不能加入斷言來驗證結果 – 必須在建立測試用例以後才能使用。

注: 讀者可以在
下載區
的 bookstore-soapui-project.zip 找到完整的例子,下文中主要以該例子為基礎進行講解。讀者解壓 zip 檔案以後,能得到一個 xml 檔案,可以通過 soapUI 的 File->import project 把專案匯入到自己的工作區中。

圖 1. soapUI 中的層次結構

soapUI 中的層次結構

對於測試用例來講,同樣是一個層次結構:

  • TestSuite:類似於 Junit 中的測試套件,其中可以加入多個 TestCase
  • TestCase:可以包含多個 TestStep
  • TestStep:一個 TestCase 可以包含多個 TestStep,TestStep 有多種型別,它可以是上面提到一個 REST 操作請求,也可以是一個 Groovy 的指令碼,還可以試一個設定屬性的操作。 TestStep 甚至支援分支跳轉操作:根據特定的條件,從一個 step 可以跳轉到其他 step, 而不必順序執行。

soapUI 實際上是一個平臺,它支援強大的程式設計能力,開發或者測試人員可以利用 groovy 指令碼來訪問 soapUI 中的物件,在執行時修改 REST request/response, 這就提供了極大的靈活性。

圖 2. TestCase 定義

TestSuite

建立測試用例

有了上面的基本概念以後,在 soapUI 中建立測試用例就比較簡單了, 使用者幾乎可以根據自己的直覺來一步一步的完成一個測試。圖 3展示的就是一個建立書籍列表 REST 服務的步驟:

1. 新建一個名為 BookStoreTest 的專案
2. 在專案上點選右鍵,選擇”New Rest Service”,在對話方塊中輸入 Service Name(BookStoreService) 和 Endpoint(http://localhost:9080)
3. 在”BookstoreService”上點選右鍵,選擇“New Resource”, 在對話方塊中輸入 Resource Name(BookList) 和 Resource Path (/books),點選 OK

4. 在彈出的對話方塊中輸入 Method Name: GetBookList,HTTP Method 選擇預設的 GET, 點選 OK

圖 3. 建立一個 REST 服務

REST servive

有了 REST 服務,就可以建立 TestCase,主要有兩種方式:

  • 自動生成,步驟如下:
    (1). 右鍵點選一個 REST 服務,例如本例中的”BookStoreService”, 選擇”Generate TestSuite”
    (2). 在彈出的對話方塊中,保持預設設定, 選擇”OK”
    (3). 輸入名稱 , 例如”BookStoreService TestSuite”, 選擇”OK”即可。
  • 手工建立,步驟如下:
    (1). 在專案”BooksStoreTest”上點選右鍵,選擇“New TestSuite”, 在對話方塊中輸入”BookStoreService TestSuite”
    (2). 在 BookStoreService TestSuite 上點選右鍵, 選擇”New TestCase”, 在對話方塊中輸入”BookList TestCase”
    (3). 然後在左邊的導航欄中展開 BookList TestCase, 在“Test Steps”上點右鍵,選擇 Add Step->Rest Test Request
    (4). 在彈出的對話方塊中選擇 GetBookListRequest_XML

一個完成的 TestCase 如
圖 4
所示,使用者可以在其中加入 Assertion 對執行結果進行驗證,這也是自動化測試所必須的。

圖 4. REST TestCase

REST Test Case

使用變數

soapUI 支援使用自定義變數(Property)在 Project 中儲存和共享資料。Property 是一個命名的字串可以被 Groovy Script,Property Transfer 或者 Property-Expansion 引用, 目前所有的變數均被處理為字串。soapUI 允許在專案的各個層次中定義變數,常用的層次包括:Project,TestSuite,TestCase,Global 等。

1. 使用 Property 編輯器定義變數。
使用者可以使用 soapUI 自帶的 Property Editor 定義各個層次的變數。以 Project 變數為例,點選 BookStoreTest,在 Properties 面板中新增自定義變數,如下圖所示:

圖 5. 使用 Property 編輯器定義專案變數

Project Property

2. 使用命令列指定變數。
修改 soapUI.bat 檔案中的 Java 引數如下:

清單 x. 使用命令列指定變數
 set JAVA_OPTS=%JAVA_OPTS% -Xms128m -Xmx256m -Dsoapui.properties=properties.txt

其中,properties.txt 為指定的全域性變數檔名字,可以通過新增如下程式碼到全域性變數檔案來設定 project/testsuite/testcase 等層次的變數:

清單 x. 使用命令列指定變數
 soapui.properties.<shortened-name-of-object>=pathtopropertiesfile

<shortened-name-of-object> 為相應物件的名字。

3. 使用變數
soapUI 使用如下語法引用專案中定義的變數:

清單 x. 使用命令列指定變數
 ${[scope]propertyName[#xpath-expression]}

其中,scope 可以為 #Project#,#TestSuite#,#TestCase# ,#Global#,#System#,#MockService#,#Env#,[TestStep name]#。

驗證結果

測試用例建好之後,需要向測試用例中新增 Assertions 以便驗證結果的正確性。soapUI 支援 Response SLA, Script Assertion, Contains, XQuery Match, Schema Compliance, XPath Match 以及 Not Contains 等多種斷言來對 response 進行判斷來保證對 Web 服務高質量的測試。本文以 XPath Match 和 Script Assertion 為例來對線上書店服務返回的 XML 和 JSON
格式的 response 進行判斷。

1. 使用 XPath Match 測試請求 GetBookListRequest_XML 返回的結果中,id 為 1234 的 book 的 price 為 29.0, response 參見

程式碼清單 2

點選 TestCase 的新增 Assertions 按鈕,如
圖 4
所示。 在彈出的 Select Assertion 視窗中選擇 XPath Match 斷言,點選 OK。配置 XPath 如下圖所示:

圖 6. 使用 XPATH 測試 XML 格式的書籍列表

XPath Match

在 XPath Expression 面板中書寫用於匹配 Response 的 XPath 表示式,Expected Result 面板中填寫期望的值。soapUI 將使用 XPath Expression 面板中填寫的 XPath 表示式與 Service 呼叫結果匹配, 將匹配結果與期望值比較,如果相同則測試通過,否則失敗。

2. 使用 Script Assertion 測試請求 GetBookListRequest_JSON 返回的結果,response 參見
程式碼清單 1

Assertion 新增過程與 XPath Match 類似,在 Select Assertion 視窗中選擇 Script Assertion,並在之後彈出的 Script Assertion 視窗中書寫如下程式碼:

清單 5. 使用 Script Assertion 測試 JSON 格式的書籍列表
 //assert the response header 
assert messageExchange.responseHeaders["Content-Type"]=="application/json;charset=UTF-8"; 
assert messageExchange.responseHeaders["Cache-Control"] == "no-cache"; 
//assert the repsonse body 
def booksRoot = net.sf.json.JSONSerializer.toJSON(messageExchange.responseContent); 
def books = booksRoot.get("books"); 
//assert book detail 
assert books[0].get("book").get("id") == "1234"; 
assert books[0].get("book").get("name") == "book1"; 
assert books[0].get("book").get("price") == 29;

3. 使用 Property 測試請求 GetBookRequest_JSON 返回的結果, response 參見
程式碼清單 3

在 Script Assertion 視窗中寫入如下程式碼:

清單 6. 使用 Property 測試 JSON 格式的書籍詳情
 //get property 
def expectedID = context.expand('${#Project#book.id}'); 
def expectedName =  context.expand('${#Project#book.name}'); 
//assert the response header 
assert messageExchange.responseHeaders["Cache-Control"] == "no-cache"; 
//assert the response body 
def bookRoot = net.sf.json.JSONSerializer.toJSON(messageExchange.responseContent); 
assert bookRoot.get("id") == expectedID; 
assert bookRoot.get("name") == expectedName; 
assert bookRoot.get("price") == 29.0;

上述使用 Groovy Script 對 Service 呼叫結果進行驗證,可用的 soapUI 物件包括 :messageExchange, context 以及 log。

  • messageExchange: 當前互動 request/response 的 MessageExchange,可以用來直接訪問 message content, HTTP Headers,Attachment 等物件。
  • context: 執行當前 TestCase 的 TestRunContext 物件,具體使用方式請參見 soapUI API 文件。
  • log: 一個標準的 Log4j Logger 物件,可以用來輸出日誌。

依照上述步驟定義好 TestCase 並新增適當的斷言之後,就可以對線上書店 REST 服務進行測試。雙擊 BookStoreSerive_TestSuite, 點選 Run 按鈕來執行所有的 TestCase,結果如下圖所示:

圖 7. 執行測試用例

Run Test


效能測試

效能測試在 soapUI 中稱為 Load Test, 針對一個 soapUI 的 TestCase, 可以建立一個或多個 LoadTest, 這些 LoadTest 會自動的 把 TestCase 中的所有步驟都新增到其中, 在執行的時候,soapUI 會自動的使用多個執行緒來執行這些 TestStep,同時也會監控 它們的執行時間, 例如最短時間,最長時間,平均時間等等。這樣使用者能夠很直觀的看到 REST 服務的響應時間,從而對效能進行調優。

建立 LoadTest 非常簡單,只需要在“Load Tests”上點選右鍵, 選擇”New LoadTest”, 然後輸入名稱即可,下圖是一個針對 GetBookList 的 效能測試, 可以看到有兩個 TestStep : “GetBookList_xml” 和”GetBookList_json” , 100 個執行緒併發執行, 時間限制是 60 秒。 最後的結果是,最短時間 4 毫秒,最長時間 1204 毫秒,平均時間 20.54 毫秒。

圖 8. 效能測試

Load test

效能測試還支援斷言,使用者可以對一個 TestStep 或 TestCase 設定執行時間要求,例如平均時間大於 2 秒就認為失敗,點選
圖 8 中
中的 “LoadTest Assertions”就可以設定。 當然根據需要,使用者也可以編寫指令碼來做一些準備工作,或者清除工作。 參見
圖 8 中
的”Setup Script”和“TearDown Script”。

與 BuildForge 整合

測試可以有效的保證程式碼的質量,但是僅僅手工的、在本機上執行的 REST 服務測試時遠遠不夠的。實際上把測試作為軟體構建的一部分,加入到持續整合中去是一個常見的敏捷開發實踐,通過頻繁的,自動化的測試, 可以更有效的發現缺陷,保證程式碼質量。

IBM Rational BuildForge 是一個管理軟體構建和釋出的平臺 , 它提供了一個框架來自動化整個構建流程,不僅僅自動化單獨的任務,可以整合多種使用者現有的指令碼和工具,最大限度的保護使用者投資。作為一個框架,BuildForge 幾乎可以呼叫作業系統上的任何指令碼。本文重點不在介紹 BuildForge, 假定讀者對 BuildForge 已經比較熟悉, 不熟悉的讀者可以檢視參考資料中的相關文章。

對於 soapUI 來說,最簡單的一種整合方式就是提供命令列指令碼讓 BuildForge 呼叫。在上文中我們已經展示了通過 soapUI 的 GUI 執行 TestCase 的功能,那麼 soapUI 可不可以通過命令列完成類似的功能呢? 答案是肯定的。

soapUI 提供了一個命令列工具 testrunner.bat 來執行一個專案中的 TestSuite, 可以在 <soap_ui_home>/bin 下找到它, 它的使用非常簡單,只需要設定下面的幾個常用引數即可:

  • -s 指定要執行的 TestSuite
  • -f 指定執行結果的輸出目錄
  • -j 生成 junit 風格的 report
  • -r 執行完成以後列印一個簡單的 summary

下面這行命令就是執行 bookstore.xml 這個 soapUI 專案中的 BookstoreTestSuite, 把結果輸出到 c:\temp\reports 中。
testrunner.bat -sBookstoreTestSuite -r -j C:\developerWorks\soapui\bookstore.xml -f c:\temp\reports

有了 testrunner,把 soapUI 的測試整合到 BuildForge 中就很簡單了,只需要在 BuildForge 的專案中新增一個步驟,參見下圖:

圖 9. BuildForge 中使用 testrunner

BuildForge and testrunner


總結

從上文可以看出,soapUI 是一款強大的 Web 服務測試工具 , 它提供了便利的 GUI 介面幫助使用者對 REST 服務進行快速的測試,使用簡單,學習成本很低。 它也提供了可程式設計能力例如變數,Groovy 指令碼等, 以便讓使用者應對一些複雜的情況。 總而言之,有效的實際專案中使用 soapUI,可以極大的減輕工作量,提供工作效率。當然作為開源的工具, soapUI 仍然在不斷的發展完善過程中,本文所能介紹的也只是它的部分功能, 感興趣的讀者可以到 soapUI 網站上獲取更多資料