契約測試--pact框架使用

背景

最近剛換城市,忙于找工作,趁著等待面試結(jié)果的間隙給自己充充電袁波。把之前一直很好奇的“微服務(wù)測試”學(xué)習(xí)了一番导街,了解了一個(gè)新的概念----契約測試披泪。

接口的契約

對(duì)于一個(gè)api接口,一般會(huì)有接口文檔說明搬瑰。文檔中會(huì)規(guī)定如何調(diào)用該接口款票、如何傳參控硼,以及接口會(huì)如何響應(yīng)。其實(shí)這個(gè)就可以理解為接口的"契約"艾少。前端按照這個(gè)“契約”去調(diào)用卡乾,服務(wù)端按照“契約”返回響應(yīng)的內(nèi)容。若服務(wù)端擅自修改了返回內(nèi)容結(jié)構(gòu)缚够,就算作違反"契約"幔妨,也是造成bug的一個(gè)原因。
擴(kuò)展到微服務(wù)當(dāng)中谍椅,每個(gè)服務(wù)與服務(wù)之間的調(diào)用误堡,同樣需要遵守這種"契約",才能保證接口功能的穩(wěn)定性雏吭。

什么是契約測試锁施?

微服務(wù)架構(gòu)中,一般分為“提供者”(provider思恐,提供接口的服務(wù)) 和“消費(fèi)者”(consumer沾谜,調(diào)用接口的服務(wù))
接口文檔可以稱之為對(duì)接口契約的具體描述,主要提供給人看胀莹。而契約測試基跑,則是將契約具象為代碼/工具可識(shí)別的形式,比如json描焰、yml媳否、DSL格式。然后借助相關(guān)測試工具荆秦,根據(jù)這份契約篱竭,自動(dòng)測試“消費(fèi)者/提供者”接口是否正常。
契約測試一般分兩種步绸,一種是消費(fèi)者驅(qū)動(dòng)掺逼,一種是提供者驅(qū)動(dòng)。其中最常用的瓤介,是消費(fèi)者驅(qū)動(dòng)的契約測試(簡稱 CDC)吕喘。即由“消費(fèi)者”定義出接口“契約”,然后測試“提供者”的接口是否符合契約刑桑。

契約測試工具使用--PACT

契約測試工具貌似有不少氯质,此處介紹一個(gè)常用工具--- PACT。
pact是一個(gè)契約測試框架祠斧,目前支持java闻察、python、ruby等多種語言。
pact契約測試分為兩步:

  1. 編寫test用例辕漂,生成契約文件(不需要啟動(dòng)服務(wù))呢灶。
  2. 利用pact-verifier命令和契約文件,驗(yàn)證接口提供者是否正確 (需要啟動(dòng)提供者服務(wù))

以下為demo示例:

1钉嘹、服務(wù)A填抬,提供者

import json
from flask import Flask

app = Flask(__name__)

@app.route('/')
def get_info():
    info = {
        "name": "zhangsan",
        "age": 20
    }
    return json.dumps(info)


if __name__ == '__main__':
    app.run(port=8080)

2、服務(wù)B隧期,消費(fèi)者。(調(diào)用服務(wù)A的接口)

import json
import requests
from flask import Flask

app = Flask(__name__)

@app.route('/')
def show_info():
    res = requests.get("http://localhost:8080/").json()
    result = {
        "code":0,
        "msg":"ok",
        "data": res
    }
    return json.dumps(result)

if __name__ == '__main__':
    app.run(port=8081)

3赘娄、測試用例仆潮,用于生成契約文件

import atexit
import requests
import unittest
from pact.consumer import Consumer
from pact.provider import Provider

# 定義一個(gè)pact,消費(fèi)者是ModuleB遣臼,生產(chǎn)者是ModuleA性置,契約文件存放在pacts文件夾下
pact = Consumer('ModuleB').has_pact_with(Provider('ModuleA'), pact_dir='./pacts')
# 啟動(dòng)服務(wù)
pact.start_service()
atexit.register(pact.stop_service)

# 測試用例
class UserTesting(unittest.TestCase):

    def test_service(self):
        # 消費(fèi)者定義的期望結(jié)果
        expected = {"name": "zhangsan", "age": 20}
        # 消費(fèi)者定義的契約的實(shí)際內(nèi)容。包括請(qǐng)求參數(shù)揍堰、請(qǐng)求方法鹏浅、請(qǐng)求頭、響應(yīng)值等
        (pact
         .given('test service.')
         .upon_receiving('a request for serviceB')
         .with_request('get', '/')
         .will_respond_with(200, body=expected))
        # pact自帶一個(gè)mock服務(wù)屏歹,端口 1234
        # 用requests向mock接口發(fā)送請(qǐng)求隐砸,驗(yàn)證mock的結(jié)果是否正確
        with pact:
            res = requests.get("http://localhost:1234").json()
        self.assertEqual(res, expected)

if __name__ == "__main__":
    ut = UserTesting()
    ut.test_service()

4、實(shí)際生成的契約文件 moduleb-modulea.json

{
  "consumer": {
    "name": "ModuleB"
  },
  "provider": {
    "name": "ModuleA"
  },
  "interactions": [
    {
      "description": "a request for serviceB",
      "providerState": "test service.",
      "request": {
        "method": "get",
        "path": "/"
      },
      "response": {
        "status": 200,
        "headers": {
        },
        "body": {
          "name": "zhangsan",
          "age": 20
        }
      }
    }
  ],
  "metadata": {
    "pactSpecification": {
      "version": "2.0.0"
    }
  }
}

5蝙眶、根據(jù)契約文件季希,驗(yàn)證服務(wù)A是否正確
a. 啟動(dòng)服務(wù)A(提供者)
b. 執(zhí)行pact-verifier --provider-base-url=http://127.0.0.1:8080 --pact-url=./pacts/moduleb-modulea.json,即可驗(yàn)證接口是否符合契約

契約測試的優(yōu)點(diǎn)

  • 在開發(fā)接口前定義好契約幽纷,消費(fèi)者和提供者可以并行開發(fā)式塌。根據(jù)契約可以很容易自測,不必等到聯(lián)調(diào)時(shí)才暴露問題
  • 確保變動(dòng)的安全性和準(zhǔn)確性友浸。只要有變化峰尝,契約測試即可第一時(shí)間發(fā)現(xiàn)
  • 契約文件可以直觀追蹤接口的變化
  • 可以將契約測試集成到CI中
  • 每次只測試單個(gè)服務(wù),更容易定位問題
  • 來自于模擬服務(wù)的可靠響應(yīng)能夠降低測試的不穩(wěn)定性

契約測試與接口測試的區(qū)別

契約測試與接口測試的原理都是一樣的收恢,都是發(fā)送請(qǐng)求武学、驗(yàn)證響應(yīng)結(jié)果。但是接口測試更多的關(guān)注業(yè)務(wù)api的功能派诬、邏輯劳淆,而契約測試主要關(guān)注接口是否符合“契約”,監(jiān)控接口的變動(dòng)默赂。
契約測試相當(dāng)于將測試工作前移沛鸵,在開發(fā)階段就能自測。并且契約測試更加輕量級(jí)。

參考

微服務(wù)下的契約測試 (CDC) 解讀
契約測試之核心解惑
Pact中文參考指南

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末曲掰,一起剝皮案震驚了整個(gè)濱河市疾捍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌栏妖,老刑警劉巖乱豆,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異吊趾,居然都是意外死亡宛裕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門论泛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揩尸,“玉大人,你說我怎么就攤上這事屁奏⊙矣埽” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵坟瓢,是天一觀的道長勇边。 經(jīng)常有香客問我,道長折联,這世上最難降的妖魔是什么粒褒? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮诚镰,結(jié)果婚禮上怀浆,老公的妹妹穿的比我還像新娘。我一直安慰自己怕享,他們只是感情好执赡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著函筋,像睡著了一般沙合。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上跌帐,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天首懈,我揣著相機(jī)與錄音,去河邊找鬼谨敛。 笑死究履,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的脸狸。 我是一名探鬼主播最仑,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼藐俺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了泥彤?” 一聲冷哼從身側(cè)響起欲芹,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吟吝,沒想到半個(gè)月后菱父,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剑逃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年浙宜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛹磺。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡梆奈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出称开,到底是詐尸還是另有隱情,我是刑警寧澤乓梨,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布鳖轰,位于F島的核電站,受9級(jí)特大地震影響扶镀,放射性物質(zhì)發(fā)生泄漏蕴侣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一臭觉、第九天 我趴在偏房一處隱蔽的房頂上張望昆雀。 院中可真熱鬧,春花似錦蝠筑、人聲如沸狞膘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挽封。三九已至,卻和暖如春臣镣,著一層夾襖步出監(jiān)牢的瞬間辅愿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來泰國打工忆某, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留点待,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓弃舒,卻偏偏與公主長得像癞埠,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354