1. 程式人生 > >微服務環境下的整合測試探索(一) —— 服務 Stub & Mock

微服務環境下的整合測試探索(一) —— 服務 Stub & Mock

作者 | 章燁明

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1

杏仁醫生CTO。中老年程式設計師,關注各種技術和團隊管理。

引子

現在微服務很流行,企業架構微服務化的確能解決不少問題,但是在微服務環境下,服務之間的依賴以及由此造成的開發、測試和整合的問題,一直都是微服務最大的痛點。

傳統的解決方案是,除了測試、預釋出和生產環境,還會部署多套用於開發和整合的環境。這樣存在的問題是,只要有一組服務出現問題,就會影響其他使用該環境的團隊的日常開發和測試。而且常常出現問題後,需要耗費很多時間定位,結果還常常是因為某個服務的版本沒有同步。並且多套環境維護起來也是一個麻煩重重,即使有了容器。

這次我們一起來探索一下 API 模擬工具以及基於契約的測試,也許會是解決這個問題的一個方案。

WireMock 介紹

我們開發應用也好、服務也好,常常需要依賴後端或者服務的介面。例如開發移動應用 App,可能後端介面還在開發中,這時 App 的開發因為無法呼叫後端,很不方便。又或者程式會依賴第三方的介面,例如微信支付,在本地開發時不能直接呼叫。

這時我們就會需要一個工具來模擬這些服務,WireMock 就是這樣的一個工具,主要針對的是最常見的 HTTP 服務。

WireMock 用於開發除錯

WireMock 首先自身就是一個可以獨立執行的服務。下載 Standalone Jar 檔案後,即可可以直接執行。

java -jar wiremock-standalone-2.11.0.jar

此時可以通過 Json 對映檔案來定義 Stub 服務。例如下面是一個對映檔案,request

 部分設定匹配的 Url 路徑、請求方法及引數,如果匹配到了,則會返回response部分設定的內容。把該檔案放到 WireMock 同路徑下的mappings目錄下即可。

{
 "request" : { "urlPath" : "/api/order/find", "method" : "GET", "queryParameters" : { "orderId" : { "matches" : "^[0-9]{16}$" } } }, "response" : { "status" : 200, "bodyFileName"
: "body-order-find-1.json", "headers" : { "Content-Type" : "application/json;charset=UTF-8" } }
}

Response 的內容可以直接在對映檔案裡設定,也可以引用了另一個檔案。這裡是引用了一個名為body-order-find-1.json的檔案,該檔案放置在 WireMock 同路徑下的__files目錄下。

{
   "success": true, "data": { "id": 781202, "buyerId": -2, "status": 0, // 略... }
}

下面我們用 curl 測試一下。第一次我們請求的引數 orderId 無法匹配指定的正則,WireMock 會返回 Request was not matched,而且還會很貼心的告訴你最接近的匹配是什麼。

$ curl http://localhost:8080/api/order/find?orderId=abcdefghijklmnop
           Request was not matched
           =======================

----------------------------------------------
| Closest stub         | Request 
----------------------------------------------
GET                    | GET
/api/order/find        | /api/order/find
----------------------------------------------

第二次我們引數 orderId 匹配的話,WireMock 會直接返回設定的結果。

$ curl http://localhost:8080/api/order/find?orderId=9999999999999999
{
    "success": true,
    "data": {
        "id": 781202,
        "buyerId": -2,
        "status": 0
    }
}

上面的例子是 WireMock 最基本的用法,除了請求匹配響應,WireMock 也能支援:

  • 通過 RESTFul 的介面提交和管理請求對映和相應。

  • 支援響應模板,返回內容時會將變數填充到響應模板中。當然,這裡的模板功能是比較簡單的,但對於大部分 Stub 的場景應該是足夠了。

  • 支援模擬異常返回,例如設定有一定比例的超時返回等等,這個功能用於測試非常方便。

為了方便編寫請求對映檔案,WireMock 還可以執行在代理模式,只需要執行時新增 --enable-browser-proxying引數即可。此時 WireMock 匹配到請求後,不是返回指定的內容,而是把請求 Forword 到指定的 URL,獲得 Response 後再返回給呼叫方。同時,WireMock 會記錄請求和返回的內容,生成 Json 對映檔案。使用時只要根據需求對這些對映檔案做一定修改,既可以用來模擬目標服務。

WireMock 用於整合測試

除了獨立執行,WireMock 也可以直接嵌入到程式碼中。最方便的就是在 JUnit 中使用,WireMock 提供了 WireMockRule, 可以很方便的在測試時嵌入一個 Stub 服務。

下面是一個支付相關的整合測試,被測試方法會呼叫微信的支付服務。stubForUnifiedOrderSuccess 設定了一個很簡單的 Stub,一旦匹配到請求的 URL 為 /pay/unifiedorder,那就返回指定的 XML 內容。這樣我就可以在整合測試裡測試整個支付流程,而不必依賴真正的微信支付。當然,測試時微信支付介面的 Host 也要改成 WireMockRule 配置的本地埠。並且,通過這種方式也很容易測試一些異常情況,根據需要修改 Stub 返回的內容即可。

public class OrderTest {
   @Rule public WireMockRule wireMockRule = new WireMockRule(9090); /**    
    * 統一下單 Stub
   
    * 參考 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
   
    *
   
    * @param tradeType 交易型別, 可以是JSAPI、NATIVE或APP
   
    */
public void stubForUnifiedOrderSuccess(String tradeType) { String unifiedOrderResp = "<xml>\n" + "    <return_code><![CDATA[SUCCESS]]></return_code>\n" + "    <return_msg><![CDATA[OK]]></return_msg>\n" + "    <appid><![CDATA[wxxxxxxxxxxxxxxxxx]]></appid>\n" + "    <mch_id><![CDATA[9999999999]]></mch_id>\n" + "    ...... \n" + "    <trade_type><![CDATA[" + tradeType + "]]></trade_type>\n" + "</xml>"; stubFor(post(urlEqualTo("/pay/unifiedorder")) .withHeader("Content-Type", equalTo("text/xml;charset=UTF-8")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "text/plain") .withBody(unifiedOrderResp))); } @Test public void test001_doPay() { stubForUnifiedOrderSuccess("JSAPI"); payServices.pay(); // 測試程式碼... }
}

有時候在整合測試裡,我們還需要驗證系統的行為,例如是否呼叫了某個 API,呼叫了幾次,呼叫的引數和內容是否符合要求等。區別於前面說的 Stub,其實這就是常說的 Mock 功能。WireMock 對此也有很強大的支援。

verify(postRequestedFor(urlEqualTo("/pay/unifiedorder"))
       .withHeader("Content-Type",equalTo("text/xml;charset=UTF-8"))
.withQueryParam("param", equalTo("param1")) .withRequestBody(containing("success"));

這樣,有了 WireMock,整合測試時處理第三方的依賴就非常方便了。不需要直接呼叫依賴的服務,也不需要專門建立用於整合測試的 Stub 或 Mock,直接程式碼中根據需要設定即可。

WireMock 總結

總結一下, WireMock 可以:

  • 作為代理執行,此時可以錄製請求和返回的指令碼,用於後繼 Stub 和 Mock 使用。

  • 獨立執行,作為一個 Stub 服務,根據匹配的請求返回資料。

  • 作為 Stub,通過程式碼嵌入 HTTP 模擬服務,在指定埠監聽,並根據匹配的請求返回資料。

  • 作為 Mock,在單元測試和整合測試中,驗證請求邏輯。例如是否進行了呼叫、引數是否正確等。

這裡再強調下 Stub 和 Mock 的區別,很多人經常搞混。Stub 就是一個純粹的模擬服務,用於替代真實的服務,收到請求返回指定結果,不會記錄任何資訊。Mock 則更進一步,還會記錄呼叫行為,可以根據行為來驗證系統的正確性。

我們可以用 WireMock 來優化開發和整合的流程。

  • 在外部服務尚未開發完成時,模擬服務,方便開發。

  • 在本地開發時,模擬外部服務避免直接依賴。

  • 在單元測試中模擬外部服務,同時驗證業務邏輯。

契約式測試

本文主要以 WireMock 為例介紹了 API 模擬工具的使用方法。其實除了 WireMock,還有不少類似的工具,例如最早的 MounteBank,以及 MockServer、Moco 等也都是很強大的工具。

不過,在微服務環境下,光有 API 模擬工具還不夠。對於 WireMock,首先必須考慮如何來管理大量的對映檔案。一個方法是開發一個專用的 Stub 平臺,來管理所有的對映檔案,同時作為 Stub 執行。另外一個方法是通過 Git 來管理對映檔案,需要的時候同步下來執行 WireMock 即可。

另外,我們上面提到 WireMock 的兩大作用,呼叫方模擬服務以及服務方整合測試,是否可以統一兩者呢?也就是說,呼叫方和服務方約定好介面,生成對映檔案,這個檔案即可以用於客戶端模擬服務,也可以用於服務方整合測試,這樣雙方開發也好、整合也好都會方便很多。下一篇我們來研究一下 Spring Cloud Contract,它就是基於 WireMock 實現了契約式的測試,上文中雙方約定好的介面,其實就是雙方的契約。

 全文完

以下文章您可能也會感興趣:

我們正在招聘 Java 工程師,歡迎有興趣的同學投遞簡歷到 [email protected]

0?wx_fmt=png

杏仁技術站

長按左側二維碼關注我們,這裡有一群熱血青年期待著與您相會。


相關推薦

【vue】vue根據不同環境(正式、測試)打包

前提姿勢 獲取終端中輸入的命令 下面的這個在webpack中會有個process物件 ,看下面圖就知道使用 process.argv.splice() 就可以獲取輸入命令引數了 此處教程區分介面 這裡是通過不同命令將修改介面前部分的地址或者修改

App自動化測試探索借助Appium實現APP的自動化測試

網絡 辦公 EDA 同時 修改 社區 重新 環境準備 測試框架 移動應用測試十大要領: 選擇系統平臺 選擇測試設備的品牌 註意行業和設備區分 關註Android的更新 不要忘記老設備 靈活使用Web分析工具 註意區分地區、運營商和網絡技術 掌握只能手機的屏幕分辨率分布情況

spring4.2.9 java專案環境ioc原始碼分析——執行refresh之前

本系列文章講述spring IOC容器如何載入Bean與例項化Bean以及其中所穿插的一些實現。本文章以ClassPathXmlApplicationContext為起點,debug啟動流程。程式碼如下public static void main(String[] args

【linux】linux 環境 安裝禪道轉載 -- 跟web服務器無關,無視apache、nginx!!!

sdn php 修改 鏈接 net 壓縮 操作 tps 數據庫 參考文章 鏈接 :https://blog.csdn.net/xinxin19881112/article/details/46813991 講的非常完美、透徹,不像其他的文章,都是抄襲的,沒一點註意事項和自己

App自動化測試探索MAC環境搭建iOS+Python+Appium測試環境

code -s image ios 使用 usr developer contents gis 環境搭建要求,MAC 機器一臺,要求 Xcode 8.0以上 1. 安裝 Homebrew /usr/bin/ruby -e "$(curl -fsSL https://raw

Ribbon在專案中的使用++單個服務環境測試+多個服務環境測試+使用官方的負載均衡策略+自定義負載均衡策略

測試中使用到的程式碼到在這裡https://download.csdn.net/download/zhou920786312/10853300   客戶端 新增依賴包      <!-- Ribbon相關 -->

關於“用Python和OpenCV建立一個圖片搜尋引擎的完整指南”在win7作業系統python3.6環境測試探索

關於“用Python和OpenCV建立一個圖片搜尋引擎的完整指南”在win7作業系統python3.6下的測試探索 原部落格文章連結:http://blog.csdn.net/kezunhai/article/details/46417041 ---------------

基於容器服務的PaaS雲平臺設計 實現容器服務和持續集成

顯示 一次 target 全部 ext neu openshift svn客戶端 enc 版權聲明:本文為博主原創文章,歡迎轉載,轉載請註明作者、原文超鏈接 ,博主地址:http://www.cnblogs.com/SuperXJ/ 前言:關於什麽是容器微服務Paa

服務之springCloud和docker-Eureka

決策 config snap service 每次 entry eas pen end 前言 本文記錄怎麽搭建服務註冊中心eureka,然後打包成docker鏡像,用docker swarm部署eureka集群 1、環境spring boot 1.5.1.RELEASE,是

配置mac環境的JAVA_HOME 與 配置maven Mac上jdk的配置 (四)在terminal中執行.class檔案

(一)mac環境下,echo $JAVA_HOME 一般輸出為空,但有時候某些構件會需要有javahome的配置,這時就需要把Java home配置好。 步驟: 1, 命令列輸入: /usr/libexec/java_home 我的環境輸出是 /Library/Java/JavaVi

ubuntu 18.04greenplum安裝筆記Linux基礎環境的搭建

背景 需要構建一個用於資料倉庫的分散式資料庫叢集。 每一個節點暫時不需要進行備份,同時也不考慮壞掉的情況。 每一個數據節點最好都不用進行過多的配置,安裝起來方便。 Greenplum的Shared-Nothing的設計思路很適合我目前的業務場景。 物理環境 4檯安裝了Linux的主機,安裝的作業系統的版本均為

服務Springcloud超詳細教程+實戰

如在文件中遇到什麼問題請聯絡作者 QQ:1172796094 本人正在找深圳Java實習工作,求大佬帶飛 —————————————————————————————————————— 認識微服務 系統架構演變 集中式架構/單體應用 垂直拆分

CentOS搭建Teuthology Ceph自動化測試平臺

Paddles及資料庫部署 安裝相關軟體 這李只列出一些必用的,每個人使用的環境不一樣,可能還會存在一些包沒有安裝的,搭建環境過程中,注意看輸出的日誌資訊,缺少什麼就安裝。 #yum install python-virtualenv postgresql po

fastText(二):博短文字fastText的應用

眾所周知,微博中的內容以短文字居多,文字內容隨意性極強,這給建模增加了很大的難度。針對這一問題,這裡分享一下fastText在微博短文字的應用。 任務目標簡單介紹一下整個任務的目標:給微博內容打上標籤,例如美妝、寵物用品等。這類問題可以轉化為經典的多分類問題。然而微博內容較短,並且文字隨意性極強,這給整個建

基於 DevOps 的服務生態系統與工程實踐

本文來自於 GOPS 2017 深圳站的演講《基於 DevOps 的微服務生態系統與工程實踐》,DevOps時代公眾號連續三篇文章詳細解讀DevOps與微服務的祕密。 作者簡介: 王磊 華為 中央軟體院首席軟體工程技術專家 國內首批 DevOps Master 認證講師,《DevOps Handb

golang在linux環境自定義包招解決問題

由於是go語言新手,有些簡單的問題處理起來有點困難。現在從go安裝到執行詳細地說起 關於go語言在linux環境下自定義包的問題困擾了我好大一會。 下面我就從前往後詳細地說一下解決方法,一招解決問題。 (I)linux下解決,從go安裝開始說起 命令安裝比直接在先下載golang

IT運維服務中的一些工作思路探索

再引用一句老話:綱舉目張!,IT運維工作要有成效,需讓我們又見樹木又見森林,於是我把有關運維工作開展思路、方法及原則進行歸納整理,與大家一起探討。 一、 運維工作目標 運維工作的核心目標是保持並提高使用者滿意度,從服務中挖掘服務,各項運維工作就應該緊密圍繞該目標開展。在此

spring4.2.9 java專案環境ioc原始碼分析——refresh之obtainFreshBeanFactory方法@2處理Resource、載入Document及解析前準備

接上篇文章,上篇文章講到載入完返回Rescouce。先找到要解析的程式碼位置,在AbstractBeanDefinitionReader類的loadBeanDefinitions(String location, Set<Resource> actualResou

spring4.2.9 java專案環境ioc原始碼分析——refresh之obtainFreshBeanFactory方法@4預設標籤bean,beans解析、最終註冊

接上篇文章,解析了import和alias標籤,只是開胃菜比較簡單,下面介紹bean標籤的載入,也是預設名稱空間下解析的重點。protected void processBeanDefinition(Element ele, BeanDefinitionParserDeleg