如何設計契約測試
常見的契約測試工具
Pact
python版本pact-python
Pacto
Sprint Cloud Contract
Pact是最常用的契約測試工具
Pact基本工作流程
基於消費者的業務邏輯,生成契約文件



寫代碼主要寫獲取Pact文件(圖一)的代碼
模擬消費者向生產者發請求有相應的工具直接運行
Pact-Python安裝
進入github官網
https://github.com/pact-foundation/pact-python/

用命令行安裝

如果碰到報錯
那就是在pact\bin目錄下缺少ZIP壓縮文件
解決方案:
進如pact包下載官網,找到對應的ZIP包自行下載,放到pact\bin目錄下,再重新用命令安裝
https://github.com/pact-foundation/pact-ruby-standalone/releases

Pact 設計用例
寫一個例子
創建一個生產者和兩個消費者

miku.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>消費者 Miku</title>
</head>
<body>
<h1 class = 'cover-heading'>{{result['name']}}</h1>
<p class="lead">郵箱:{{result['contact']['Email']}}</p>
<p class="lead">電話:{{result['contact']['Phone Number']}}</p>
</body>
</html>
nanoha.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>消費者 Miku</title>
</head>
<body>
<h1 class = 'cover-heading'>{{result['name']}}</h1>
<p class="lead">郵箱:{{result['contact']['Email']}}</p>
<p class="lead">電話:{{result['contact']['Phone Number']}}</p>
</body>
</html>
用flask寫一個mock接口
用來當做生產者
api_service.py
#! usr/bin/env python
# _*_ coding: utf-8 _*_
from flask import Flask,request,jsonify
app = Flask(__name__)
app.config['JSON_SORT_KEYS'] = False
rsp_body = [
{
"salary": 45000,
"name": "Hatsune Miku",
"nationality":"Japan",
"contact": {
"Email": "hatsune.miku@woniuxy.com",
"Phone Number": "13900110001"
}
},{
"salary": 80000,
"name": "Takamachi Nanoha",
"nationality":"Japan",
"contact": {
"Email": "takamachi.nanoha@woniuxy.com",
"Phone Number": "18800880008"
}
}
]
@app.route('/information',methods=['GET'])
def test():
get_name = request.args.get("name","").lower()
if get_name == 'miku':
rsp = jsonify(rsp_body[0])
elif get_name == 'nanoha':
rsp = jsonify(rsp_body[1])
else:
rsp = jsonify({'status': '404 not found.'})
return rsp
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
同樣用flask寫兩個消費者consumer_miku.py 和 consumer_nanoha.py
consumer_miku.py
from flask import Flask,request,jsonify,render_template
import urllib3
import json
app = Flask(__name__)
@app.route('/miku',methods=['GET'])
def miku_html():
params = {"name": "miku"}
http = urllib3.PoolManager()
resp = http.request('GET', 'http://localhost:8080/information', params)
result = json.loads(resp.data.decode())
return render_template("miku.html", result = result)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8081)
consumer_nanoha.py
from flask import Flask,request,jsonify,render_template
import urllib3
import json
app = Flask(__name__)
@app.route('/nanoha',methods=['GET'])
def nanoha_html():
params = {"name": "nanoha"}
http = urllib3.PoolManager()
resp = http.request('GET', 'http://localhost:8080/information', params)
result = json.loads(resp.data.decode())
return render_template("nanoha.html", result = result)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8082)
運行生產者和消費者,然后打開URL

pact-verifier --provider-base-url=http://localhost:8080 --pact-url=consumer_miku-provider.json

創建第一個Pact契約測試用例
先做一個公共方法
作用:用來實現我們一個發請求的方法
query.py
#! usr/bin/env python
# -*- coding: utf-8 -*-
import requests
def get_cartoon_characters(name):
# pact作為模擬生產者時,其端口默認為1234
resp = requests.get('http://localhost:1234/information',{'name':name})
return resp
我們做契約測試,首先是模擬生產者,'1234'是pact默認的端口號
寫一個契約測試用例
contract.py
#! usr/bin/env python
# -*- coding: utf-8 -*-
import atexit
import unittest
from pact import Consumer,Provider
from microservice.Contract_test.query import get_cartoon_characters
# 構造pact對象,定義消費者服務的名字並給它一個生產者服務
pact = Consumer('Consumer Miku').has_pact_with(Provider('Provider'))
pact.start_service()
# 注冊退出的時候關閉pact服務
atexit.register(pact.stop_service)
class GetMikuInfoContract(unittest.TestCase):
def test_miku(self):
# 定義響應期望的結果
expected = {
"salary": 45000,
"name": "Hatsune Miku",
"nationality": "Japan",
"contact": {
"Email": "hatsune.miku@woniuxy.com",
"Phone Number": "13900110001"
}
}
# 定義響應頭
headers = {
"Content-Type":"application/json"
}
# 定義模擬生產者提供者接受請求以及響應的方式
(pact
.upon_receiving('a request for Miku')
.with_request(
method='GET',
path='/information',
query={'name':'miku'}
).will_respond_with(200,headers,expected))
# 定義消費者服務向模擬生產者發出請求並活得響應
with pact:
result = get_cartoon_characters('miku')
# 做最后的斷言
self.assertEqual(result.json(),expected)
if __name__ == '__main__':
unittest.main(verbosity=2)

運行
先運行生成一個契約文件(.json文件)

先運行生產者api_service.py

打開終端,進入契約腳本目錄,運行命令
pact-verifier --provider-base-url=http://localhost:8080 --pact-url=consumer_miku-provider.json pact-verifier --provider-base-url=http://localhost:8080 --pact-url=consumer_miku-provider.json

測試結果通過,如下圖:

