微服務環境下的整合測試探索(一) —— 服務 Stub & Mock
作者 | 章燁明
杏仁醫生CTO。中老年程式設計師,關注各種技術和團隊管理。
引子
現在微服務很流行,企業架構微服務化的確能解決不少問題,但是在微服務環境下,服務之間的依賴以及由此造成的開發、測試和整合的問題,一直都是微服務最大的痛點。
傳統的解決方案是,除了測試、預釋出和生產環境,還會部署多套用於開發和整合的環境。這樣存在的問題是,只要有一組服務出現問題,就會影響其他使用該環境的團隊的日常開發和測試。而且常常出現問題後,需要耗費很多時間定位,結果還常常是因為某個服務的版本沒有同步。並且多套環境維護起來也是一個麻煩重重,即使有了容器。
這次我們一起來探索一下 API 模擬工具以及基於契約的測試,也許會是解決這個問題的一個方案。
WireMock 介紹
我們開發應用也好、服務也好,常常需要依賴後端或者服務的介面。例如開發移動應用 App,可能後端介面還在開發中,這時 App 的開發因為無法呼叫後端,很不方便。又或者程式會依賴第三方的介面,例如微信支付,在本地開發時不能直接呼叫。
這時我們就會需要一個工具來模擬這些服務,WireMock 就是這樣的一個工具,主要針對的是最常見的 HTTP 服務。
WireMock 用於開發除錯
WireMock 首先自身就是一個可以獨立執行的服務。下載 Standalone Jar 檔案後,即可可以直接執行。
java -jar wiremock-standalone-2.11.0.jar
此時可以通過 Json 對映檔案來定義 Stub 服務。例如下面是一個對映檔案,request
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] 。
杏仁技術站
長按左側二維碼關注我們,這裡有一群熱血青年期待著與您相會。
相關推薦
【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.04下greenplum安裝筆記(一)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