首页 > 技术文章 > 契约测试Pact(四)

zibinchen 2020-05-19 10:05 原文

如何设计契约测试

常见的契约测试工具

Pact

python版本 pact-python

Pacto

Sprint Cloud Contract

Pact是最常用的契约测试工具

Pact基本工作流程

基于消费者的业务逻辑,生成契约文件



写代码主要写获取Pact文件(图一)的代码

模拟消费者向生产者发请求有相应的工具直接运行

Pact-Python安装

进入github

https://github.com/pact-foundation/pact-python/

用命令行安装

pip install 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

测试结果通过,如下图:

推荐阅读