自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

聊一聊契約測試

開發(fā) 開發(fā)工具
契約測試不是銀彈,它不是替代E2E測試的終結(jié)者,更不是單元測試的升級換代,它更偏向于服務和服務之間的API測試,通過解耦服務依賴關(guān)系和單元測試來加快測試的運行效率。

一、什么是契約

如果從契約產(chǎn)生的階段來說,現(xiàn)有資料表明最早要追溯到西周時期的《周恭王三年裘衛(wèi)典田契》,將契約文字刻寫在器皿上,就是為了使契文中規(guī)定的內(nèi)容得到多方承認、信守,“萬年永寶用”。所以訂立契約的本身,就是為了要信守,就是對誠信關(guān)系的一種確立。誠信,是我國所固有的一種優(yōu)良傳統(tǒng),也是延續(xù)了幾千年的一種民族美德,在中國儒家的思想體系里,是倫理道德內(nèi)容中的一部分。

[[232064]]

《現(xiàn)藏于臺北故宮博物院》

現(xiàn)實真的是那么美好嗎?小時候的價值觀教育未能改變社會的現(xiàn)狀,缺少契約精神的案例卻比比皆是。

那么,契約真的要消失了嗎?不盡然,在軟件測試領域,我們又重新拾起了契約這把利器。

二、發(fā)展歷程

接下來讓我們回顧一下契約測試的起源和發(fā)展歷程:

假設我們有這樣一個場景:A團隊負責開發(fā)API服務,B團隊進行API調(diào)用消費服務。

為了保證API的正確性,我們會對外部系統(tǒng)的API進行測試(除非你100%相信外部系統(tǒng)永遠正確和保持不變),這很可能就會導致一個問題,當外部系統(tǒng)并不那么穩(wěn)定或者請求時間過長時,就會導致我們的測試效率很低,并且穩(wěn)定性下降。比如當外部API掛掉導致測試失敗時,你并不能完全確信是API功能被更而改導致的失敗還是運行環(huán)境不穩(wěn)定導致的請求失敗。

最初,解決這個問題的方案是構(gòu)建測試替身(Test Double),通過模擬外部API的響應行為來增強測試的穩(wěn)定性和反應速度。實現(xiàn)手段是在測試環(huán)境中搭建一個模擬服務環(huán)境,通過設定一些請求參數(shù)來返回不同的響應內(nèi)容,然后再被內(nèi)部系統(tǒng)調(diào)用,來保證調(diào)用端的正確性。構(gòu)建模擬環(huán)境時我們可以使用幾種不同的測試手段,如Dummy,F(xiàn)ake,Stubs,Spies,Mocks等。可是,問題又來了,如果使用測試替身那如何能保證外部系統(tǒng)API變化時得到及時的響應,換句話說,當內(nèi)部系統(tǒng)測試都通過的通過時,如何能保證真正的外部API沒有變化?

一個比較簡單的方式是部分測試使用測試替身,另外一部分測試定期調(diào)用真實的外部API,這樣既保證了測試的運行效率、調(diào)用端的準確性,又能確保當真實外部系統(tǒng)API改變時能得到反饋。

是不是到這里就皆大歡喜了呢?

如果劇情到這里就結(jié)束的話,未免太過俗套。這個方案最大的缺陷在于API的反應速度,真實外部API的反饋周期過長,如果減少真實API測試間隔時間就又會回到文章最開始的兩難境地。

那么如何解決這個問題呢?先來讓我們剖析一下前面幾種解決方案的共通點。

在上面的場景中,我們都是已知外部API功能來編寫相應的功能測試,并且使用直接調(diào)用外部API的方式來達到驗證測試的目的,這樣就不可避免的帶來兩個問題:

  • 第一,服務消費方對服務提供方API的更改是通過對API的測試來感知的。
  • 第二,直接依賴于真實API的測試效果受限于API的穩(wěn)定性和反映速度。

解決方式首先是依賴關(guān)系的解耦,去掉直接對外部API的依賴,而是內(nèi)部和外部系統(tǒng)都依賴于一個雙方共同認可的約定—“契約”,并且約定內(nèi)容的變化會被及時感知;其次,將系統(tǒng)之間的集成測試,轉(zhuǎn)換為由契約生成的單元測試,例如通過契約描述的內(nèi)容,構(gòu)建測試替身。這樣,依賴契約的測試效率優(yōu)于集成測試,同時契約替代外部API成為信息變更的載體。

對于契約來講,行業(yè)內(nèi)比較成熟的解決方案是基于YAML標記語言的Swagger Specification(OpenAPI Specification),或者是基于JSON格式的Pact Specification。

通常的做法是API的提供者使用“契約”的形式,將功能發(fā)布在公共平臺,給調(diào)用方進行說明和參考,這里我們可以暫時稱之為Provider-Driven-Contract。這種做法的潛在問題是,功能提供方的API返回內(nèi)容是否都滿足所有API調(diào)用者的需求不得而知。所以,針對這個問題,依賴關(guān)系再一次反轉(zhuǎn),契約測試就搖身一變成為了Consumer-Driven-Contract test(CDCT), 通過給API提供方提供契約的形式,來完成功能的實現(xiàn)。

難道CDCT成為了問題終結(jié)者嗎?請聽后面分解。

注: 契約測試其中一個的典型應用場景是內(nèi)外部系統(tǒng)之間的測試,另一個典型的例子是前后端分離后的API測試,這里不做過多展開。

三、契約測試的維度

1. 測試覆蓋范圍對比(縱向)

  • 單元測試:對軟件中的基本組成單位的測試,大多數(shù)是方法函數(shù)的測試,運行速度快。
  • 契約測試:對服務之間的功能進行的測試,運行速度基本與單元測試相同。
  • E2E 測試:對系統(tǒng)前后端或者不同系統(tǒng)之間的集成測試,大多通過模擬UI操作的方式實現(xiàn),運行速度三者之中最慢。

2. 測試效率對比(橫向)

環(huán)境依賴:

  • 單元測試:程序集
  • 契約測試:程序集、依賴契約文件、虛擬路由服務
  • 端到端測試:程序集、真實路由服務、前端UI
  • 運行速度: 單元測試 > 契約測試 > 端到端測試

Pact官方給出的幾個場景:

適用場景:

  • 團隊能把控開發(fā)過程中的Consumer和Provider端
  • 適合Consumer驅(qū)動開發(fā)的場景
  • 對于每個獨立的Consumer端,Provider端都能管理好需求。

不適用的場景:

  • 公共API或者是OAuth授權(quán)服務
  • Provider端和Consumer端沒有良好的溝通渠道
  • 針對性能的測試
  • Provider端的功能性測試(Pact只測試內(nèi)容和請求格式)
  • 對于不同輸入有相同的輸出,并未達到驗證的目的
  • 當前測試輸入需要依賴之前測試返回的結(jié)果

以上對比說明契約測試所要解決的問題是替代系統(tǒng)之間的集成測試,通過契約和單元測試的方式加速系統(tǒng)運行。同時也說明契約測試存在一些不適用的場景,要依據(jù)使用場景區(qū)別對待。契約測試沒有取代單元測試以及E2E測試。

四、契約測試與CD的整合

最開始,我們的pipeline是這樣的,單元測試是獨立的測試,當通過單元測試后運行集成測試。此時集成測試成為了系統(tǒng)瓶頸,而且一旦集成測試失敗,就必須被迅速修復,其他pipeline只能等待其修復,否則任何新的變更都會測試失敗。

一個解決辦法是將集成測試分散在每個pipeline上,每次集成測試運行的版本是當前的最新代碼和其他系統(tǒng)的上一次通過版本之間的測試。這樣解決了測試的獨立性以及不會阻礙其他pipeline測試的效果,然后將通過測試的不同系統(tǒng)的package按照版本保存。但是這樣一來,集成測試的缺點就更為明顯提現(xiàn)出來,第一是系統(tǒng)部署時間長,每次集成測試需要運行同樣的測試在不同pipeline上,增加了測試成本和反饋周期。

接下來,我們使用契約測試替代集成測試。這樣有幾點好處不僅解決了獨立測試的目的,同時解決了集成測試慢和部署時間長等問題。

為了保證契約測試的正確性,契約文件由Consumer端生成,然后Provider端來實現(xiàn)API,我們使用CDCT來改造我們的pipeline。

我們先假設B系統(tǒng)希望A系統(tǒng)提供新功能,如果按照圖中黃色步驟來提交的話,則會測試失敗,原因在于此時,契約文件是最新的B-A.consumer.1.1.pact與之對應A-B.provider.1.0.jar不是最新的,所以測試失敗。

按照圖中步驟2運行,當提交A的pipeline時,當前版本的A已經(jīng)升級到1.1,而契約文件還是1.0版本,沒有break測試的情況下,最終將A-B.provider.1.1.jar提交到服務器上。

 

然后按照圖中步驟3運行,A-B.provider.1.1.jar和B-A.consumer.1.1.pact完美契合,最終又將B-A.consumer.1.1.pact提交到服務器。所以,改成CDCT之后,雖然產(chǎn)生了一定的提交順序依賴,但是帶來的更多的好處是確保契約文件的產(chǎn)生是調(diào)用端提出,并且保證當前最新,確保系統(tǒng)的正確性。

喜歡思考的同學不難發(fā)現(xiàn),CDCT存在自身的缺陷,一個簡單的例子是當B存在一個已有的契約約束A的一個功能,當B需要A更新其API時,是先提交B的契約測試,還是更改A的功能到最新版本?其實二者都不可行。

解決辦法萬變不離其宗,就是大家熟悉的不能再熟悉的重構(gòu)心法,由王建總結(jié)的十六字箴言:

[[232073]]

我們分五步來完成API的更新:

  • Provider端提交一個新的API來保證新功能,同時舊的API功能不變,提交并通過測試。
  • 將Consumer端API的調(diào)用指向Provider端的新API,并更新契約文件以約束新功能。
  • 將Provider端舊API同步更新為新API,提交并通過測試。
  • 將Consumer端指回舊有API,其他保持不變。
  • 將Provider端臨時過渡的新API刪除。

至此,我們解決了API更新時如何保證契約測試的提交順序,如果是刪除API,則直接刪除Consumer端的契約測試即可。

五、需要思考的問題

1. 如果并行測試的話,誰先提交成功的版本,另外一個測試是否要重新運行?

設想,當兩個并行pipeline A和B,同時運行時,A中跑的是A1.1和B1.0,B中跑的是B1.1和A1.0的測試,假如雙方均能通過各自的測試,但是新版本不兼容(A1.1和B1.1測試失敗),雙方都將各自的新版本保留,這樣就造成了存在相互不兼容的兩個版本。目前解決方案是,人為制造一個“瓶頸”,保證同時只有一個契約測試在運行,保存的只有一個版本。

2. 契約測試可維護性如何?

構(gòu)建契約測試類似于單元測試,并且在Pact的框架下十分方便維護。但是,測試框架本身還有一些問題,諸如,大小寫敏感,空值驗證,只有一份契約文件,契約測試分組等。

(以上是基于pact 1.0的實踐,pact2.0使用了正則表達式以及TypeMatching等機制解決了驗證“具體”值的問題,更多詳細內(nèi)容請關(guān)注pact官方文檔)

六、結(jié)語

契約測試不是銀彈,它不是替代E2E測試的終結(jié)者,更不是單元測試的升級換代,它更偏向于服務和服務之間的API測試,通過解耦服務依賴關(guān)系和單元測試來加快測試的運行效率。

【本文是51CTO專欄作者“ThoughtWorks”的原創(chuàng)稿件,微信公眾號:思特沃克,轉(zhuǎn)載請聯(lián)系原作者】

戳這里,看該作者更多好文

責任編輯:趙寧寧 來源: 51CTO專欄
相關(guān)推薦

2018-01-10 14:13:04

測試矩陣API測試

2023-11-04 16:28:54

2020-01-03 11:04:54

安全測試滲透

2021-01-28 22:31:33

分組密碼算法

2023-09-22 17:36:37

2020-05-22 08:16:07

PONGPONXG-PON

2023-07-26 07:24:07

2021-08-04 09:32:05

Typescript 技巧Partial

2022-08-08 08:25:21

Javajar 文件

2022-11-01 08:46:20

責任鏈模式對象

2018-11-29 09:13:47

CPU中斷控制器

2019-02-13 14:15:59

Linux版本Fedora

2021-01-29 08:32:21

數(shù)據(jù)結(jié)構(gòu)數(shù)組

2021-02-06 08:34:49

函數(shù)memoize文檔

2023-05-15 08:38:58

模板方法模式

2023-07-06 13:56:14

微軟Skype

2020-10-15 06:56:51

MySQL排序

2020-09-08 06:54:29

Java Gradle語言

2022-03-08 16:10:38

Redis事務機制

2022-03-29 09:56:21

游戲版本運營
點贊
收藏

51CTO技術(shù)棧公眾號