Python接口測(cè)試實(shí)戰(zhàn)5(下) - RESTful、Web Service及Mock Server

課程目錄

Python接口測(cè)試實(shí)戰(zhàn)1(上)- 接口測(cè)試?yán)碚?/a>
Python接口測(cè)試實(shí)戰(zhàn)1(下)- 接口測(cè)試工具的使用
Python接口測(cè)試實(shí)戰(zhàn)2 - 使用Python發(fā)送請(qǐng)求
Python接口測(cè)試實(shí)戰(zhàn)3(上)- Python操作數(shù)據(jù)庫(kù)
Python接口測(cè)試實(shí)戰(zhàn)3(下)- unittest測(cè)試框架
Python接口測(cè)試實(shí)戰(zhàn)4(上) - 接口測(cè)試框架實(shí)戰(zhàn)
Python接口測(cè)試實(shí)戰(zhàn)4(下) - 框架完善:用例基類,用例標(biāo)簽,重新運(yùn)行上次失敗用例
Python接口測(cè)試實(shí)戰(zhàn)5(上) - Git及Jenkins持續(xù)集成
Python接口測(cè)試實(shí)戰(zhàn)5(下) - RESTful、Web Service及Mock Server

更多學(xué)習(xí)資料請(qǐng)加添加作者微信:superz-han獲取

本節(jié)內(nèi)容

  • REST及RESTful API
  • Web Service
  • XML解析
  • Mock Server

REST及RESTful API

參考鏈接: https://blog.csdn.net/lch2848508/article/details/72729658

REST:表述性狀態(tài)轉(zhuǎn)移或表現(xiàn)層狀態(tài)轉(zhuǎn)移,“表現(xiàn)”及每個(gè)接口地址(URI)都表現(xiàn)為(視為)一個(gè)資源對(duì)象(文本資源、圖片資源、服務(wù)資源),狀態(tài)轉(zhuǎn)移指通過POST/PUT方法發(fā)送完整的新狀態(tài)信息來更改資源對(duì)象的狀態(tài)
如某https://api.***.com/user資源狀態(tài)為{"name": "Kaka", "age": 30},我們通過POST/PUT請(qǐng)求發(fā)送新狀態(tài){"name": "Kaka", "age": 18}來更新對(duì)象信息,完成狀態(tài)轉(zhuǎn)移

URI 與URL的區(qū)別:URL值包含協(xié)議的鏈接,如https://www.baidu.com, 還有一種相對(duì)鏈接叫URN,如/doc/1.html,這兩種都能唯一定位一個(gè)資源,URI(統(tǒng)一資源定位符)包含URL和URN

RESTful API是一種接口設(shè)計(jì)風(fēng)格或規(guī)范,主要有以下特點(diǎn):

  • 統(tǒng)一使用https協(xié)議
  • 接口使用專用的api域名 https://api.example.com/
  • 接口分版本管理 https://api.example.com/v1/
  • 路徑又稱"終點(diǎn)"(endpoint),表示API的具體網(wǎng)址,路徑中只能包含名詞(代表資源對(duì)象),可以使用復(fù)數(shù)來代表多個(gè)一個(gè)資源集合https://api.example.com/v1/zoos
  • 同一個(gè)接口提供多種請(qǐng)求方法,GET獲取資源信息,POST新建或更新資源,PUT/PATCH更新資源,DELETE刪除資源
  • 可以通過url參數(shù)過濾信息 https://api.example.com/v1/zoos?limit=10 # 獲取前10個(gè)
  • 盡量使用JSON, 避免使用XML
  • 身份認(rèn)證推薦使用OAuth2.0,或Basic Auth,token等,避免使用Cookie和Session(RESTful強(qiáng)調(diào)無(wú)狀態(tài)的設(shè)計(jì))

示例:

https://api.github.com
授權(quán) Basic Auth (superhin001, ***) 或 Oauth 2.0 Access Token: 1c4f679300f29ee4e7041028d49e504b9da145b1

GET https://api.github.com/user 獲取用戶信息

GET https://api.github.com/user

POST/PATCH https://api.github.com/user 修改用戶信息

POST https://api.github.com/user

POST/PATCH 數(shù)據(jù)

{
    "login": "superhin001",
    "id": 21163682,
    "node_id": "MDQ6VXNlcjIxMTYzNjgy",
    "avatar_url": "https://avatars3.githubusercontent.com/u/21163682?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/superhin001",
    "html_url": "https://github.com/superhin001",
    "followers_url": "https://api.github.com/users/superhin001/followers",
    "following_url": "https://api.github.com/users/superhin001/following{/other_user}",
    "gists_url": "https://api.github.com/users/superhin001/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/superhin001/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/superhin001/subscriptions",
    "organizations_url": "https://api.github.com/users/superhin001/orgs",
    "repos_url": "https://api.github.com/users/superhin001/repos",
    "events_url": "https://api.github.com/users/superhin001/events{/privacy}",
    "received_events_url": "https://api.github.com/users/superhin001/received_events",
    "type": "User",
    "site_admin": false,
    "name": "我是韓老師",
    "company": null,
    "blog": "",
    "location": null,
    "email": "superhin@126.com",
    "hireable": null,
    "bio": null,
    "public_repos": 3,
    "public_gists": 0,
    "followers": 0,
    "following": 0,
    "created_at": "2016-08-22T01:12:32Z",
    "updated_at": "2018-09-14T02:33:43Z",
    "private_gists": 0,
    "total_private_repos": 0,
    "owned_private_repos": 0,
    "disk_usage": 45430,
    "collaborators": 0,
    "two_factor_authentication": false,
    "plan": {
        "name": "free",
        "space": 976562499,
        "collaborators": 0,
        "private_repos": 0
    }
}

GET https://api.github.com/user/keys 獲取用戶所有SSH-Key信息

GET https://api.github.com/user/keys

POST https://api.github.com/user/keys 新建Key

POST https://api.github.com/user/keys

POST 數(shù)據(jù)

{
        "id": 30742411,
        "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDfsTJs7mNWstJ+tO6O1jQHKdDdnldqlqkO0gAune9EH7oqICD1hP7c1duNZwvNnvyGa7SyqamIpNXmSYv303FEVAXzPsb9MzCChG16gzevQtbIX4Qt7vFOsHNSCikSCD/s6DMa0Koryiu7Yju5mW9UUnjVM+a1P80SOiK7p2UBQPFVKRrUtr0htV3U6a2rdP51Vzm2UCjChTUa4q7L3m4C7oB9aSvUsNTk+PmuJlAer4oOd7FsNPqD1Or3lRKAmgxbTX4xTaOkwibK0t2eYkh/VTUPMQ9wDwpa4hZLiEq9qSew3McCwsl70k4H0H4F/VwV2sSCXqZu274YmNDT5Hl3 hanzhichao@hanzhichao01",
        "title": "test3",
        "verified": true,
        "created_at": "2018-09-14T09:54:51Z",
        "read_only": false
}

Web Service

Web Service 是一種跨平臺(tái)(Java對(duì)象,Python也可以調(diào)用)RPC(遠(yuǎn)程方法調(diào)用)解決方案。
基于SOAP協(xié)議,使用XML這種跨平臺(tái)語(yǔ)言傳輸對(duì)遠(yuǎn)程方法的調(diào)用信息及返回結(jié)果,并提供WSDL接口描述服務(wù)

  • SOAP:簡(jiǎn)單面向?qū)ο髤f(xié)議, 基于XML語(yǔ)言,使用HTTP協(xié)議傳輸
  • XML: 可擴(kuò)展標(biāo)記語(yǔ)言,同JSON一樣是一種跨平臺(tái)語(yǔ)言
  • WSDL: Web Service服務(wù)描述語(yǔ)言,提供遠(yuǎn)程對(duì)象的調(diào)用描述信息(類似于接口文檔,XML格式)

SOAP格式

SOAP協(xié)議基于XML語(yǔ)言, SOAP消息體首先必須有個(gè)信封(Enelope),信封中可以有信息頭(Header)和信息體(Body),其中Body中還可以包含錯(cuò)誤信息(Fault)
基本格式如下:

<!-- 信封固定格式 指定命名空間為soapenv -->
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2001/12/soap-envelope" soapenv:encodingStyle="http://www.w3.org/2001/12/soap-encoding">   
    <soapenv:Header>  <!--信息頭 可選,可寫成但標(biāo)簽-->
        ......
    </soapenv:Header>
    <soapenv:Body>   <!--信息體 實(shí)際調(diào)用內(nèi)容-->
        ......
    </soapenv:Body>
</soap:Envelope>

SOAP詳細(xì)教程:http://www.runoob.com/soap/soap-header.html

使用SoupUI
示例接口: http://115.28.108.130:4000/?wsdl
由于Postman等不具備將wsdl接口信息解析成對(duì)象描述的功能,我們使用另一個(gè)SOAP接口專用的測(cè)試工具SoupUI

SoupUI下載地址: http://www.wmzhe.com/soft-32815.html

  1. 新建項(xiàng)目


    File -> 新建SOAP項(xiàng)目
填入項(xiàng)目名,WSDL地址,點(diǎn)擊OK
保存后自動(dòng)解析出所有方法(接口)信息
  1. 發(fā)送接口
填寫參數(shù)處
填寫完參數(shù),點(diǎn)擊發(fā)送按鈕,查看響應(yīng)結(jié)果

使用Fiddler抓包,查看raw格式:

POST http://115.28.108.130:4000/ HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: "addUser"
Content-Length: 370
Host: 115.28.108.130:4000
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:user="UserService">
   <soapenv:Header/>
   <soapenv:Body>
      <user:addUser>
         <!--Optional:-->
         <user:name>范冰冰</user:name>
         <!--Optional:-->
         <user:password>123456</user:password>
      </user:addUser>
   </soapenv:Body>
</soapenv:Envelope>

使用Postman發(fā)送SOAP接口

Postman發(fā)送SOAP接口

text/xml 和 application/xml的區(qū)別: 當(dāng)你<?xml version='1.0' encoding='UTF-8'>指定編碼的時(shí)候,使用application/xml會(huì)安裝xml指定的編碼傳輸,而使用text/html會(huì)默認(rèn)使用us-ascii編碼編碼傳輸數(shù)據(jù)

使用Python操作Web service接口

pip install suds-jurko

from suds.client import Client

service = Client("http://115.28.108.130:4000/?wsdl").service  # 獲取遠(yuǎn)端服務(wù)對(duì)象
result = service.addUser("范冰冰", "123456")  # 向本地方法一樣調(diào)用
print(result)  # 輸出 用戶已存在

使用requests庫(kù)發(fā)送

import requests

url = 'http://115.28.108.130:4000/'

data = '''<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:user="UserService">
   <soapenv:Header/>
   <soapenv:Body>
      <user:addUser>
         <!--Optional:-->
         <user:name>張三</user:name>
         <!--Optional:-->
         <user:password>123456</user:password>
      </user:addUser>
   </soapenv:Body>
</soapenv:Envelope>
'''.encode('utf-8')

res = requests.post(url=url,data=data)
print(res.text)

結(jié)果:

<?xml version='1.0' encoding='UTF-8'?>
<soap11env:Envelope xmlns:soap11env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="UserService"><soap11env:Body><tns:addUserResponse><tns:addUserResult>用戶已存在</tns:addUserResult></tns:addUserResponse></soap11env:Body></soap11env:Envelope>

XML解析

XML: 可擴(kuò)展標(biāo)記語(yǔ)言,使用<tag></tag>標(biāo)簽,多級(jí)樹狀結(jié)構(gòu),多用來存儲(chǔ)和傳輸數(shù)據(jù),如:

<bookstore>
    <book category="COOKING">
        <title lang="en">Everyday Italian</title>
        <author>Giada De Laurentiis</author>
        <year>2005</year>
        <price>30.00</price>
    </book>
    <book category="CHILDREN">
        <title lang="en">Harry Potter</title>
        <author>J K. Rowling</author>
        <year>2005</year>
        <price>29.99</price>
    </book>
    <book category="WEB">
        <title lang="en">Learning XML</title>
        <author>Erik T. Ray</author>
        <year>2003</year>
        <price>39.95</price>
    </book>
</bookstore>
  • 根元素(根節(jié)點(diǎn)),父元素,子元素,同級(jí)元素:bookstore為根元素,book是title的父元素,title是book的子元素,title和author是同級(jí)元素
  • 標(biāo)簽,屬性,文本:bookstore boot title等為標(biāo)簽(元素節(jié)點(diǎn)),boot中category="COOKING" category為屬性,COOKING為屬性值,<title></title>之間的Everyday Italian為文本

Python解析XML

  1. 加載元素樹(ElementTree)得到根節(jié)點(diǎn)
  2. 從根節(jié)點(diǎn)使用xpath查找其他節(jié)點(diǎn)
from xml.etree import ElementTree
d = '''<bookstore>
    <book category="COOKING">
        <title lang="en">Everyday Italian</title>
        <author>Giada De Laurentiis</author>
        <year>2005</year>
        <price>30.00</price>
    </book>
    <book category="CHILDREN">
        <title lang="en">Harry Potter</title>
        <author>J K. Rowling</author>
        <year>2005</year>
        <price>29.99</price>
    </book>
    <book category="WEB">
        <title lang="en">Learning XML</title>
        <author>Erik T. Ray</author>
        <year>2003</year>
        <price>39.95</price>
    </book>
</bookstore>
'''
root = ElementTree.fromstring(d) # 加載元素樹(ElementTree)得到根節(jié)點(diǎn)

print(root.find("."))  # 選擇當(dāng)前節(jié)點(diǎn)
print(root.find("book"))  # 選擇標(biāo)簽為book的子節(jié)點(diǎn)
print(root.find("book[2]"))  # 選擇標(biāo)簽為book的第三個(gè)子節(jié)點(diǎn)
print(root.find("book[@category='COOKING']"))  # 選擇標(biāo)簽為book切標(biāo)簽屬性為category="COOKING"
print(root.find("./book/[title='Harry Potter']"))  # 選擇標(biāo)簽為book的節(jié)點(diǎn)中包含子標(biāo)簽title 切title的文本內(nèi)容為Harry Potter

結(jié)果:

<Element 'bookstore' at 0x000002406B666688>
<Element 'book' at 0x000002406B6666D8>
<Element 'book' at 0x000002406B8600E8>
<Element 'book' at 0x000002406B6666D8>
<Element 'book' at 0x000002406B8600E8>

find()返回的是節(jié)點(diǎn)對(duì)象,可以通過.tag獲取標(biāo)簽名,.attrib獲取屬性字典,.text獲取文本

XPath選擇器

  • 路徑: 按路徑選擇
  • 標(biāo)簽名: 選定所有該名的標(biāo)簽
  • *: 選定所有元素
  • . : 選擇當(dāng)前節(jié)點(diǎn)
  • ..: 選擇上級(jí)節(jié)點(diǎn)
  • //: 選擇當(dāng)前元素下所有級(jí)別的元素
  • [@屬性名]:選擇所有具有當(dāng)前屬性的元素
  • [@屬性名=屬性值]: 選擇屬性=屬性值的元素
  • [子標(biāo)簽名]:選擇具有當(dāng)前子標(biāo)簽的元素
  • [子標(biāo)簽名='文本']: 選擇子標(biāo)簽文本的元素

常用的三種定位元素方法

  1. 從根節(jié)點(diǎn)按路徑選擇: root.find("./book[3]/title")
  2. 按屬性并結(jié)合相對(duì)路徑選擇: root.find(book[@category='WEB']/title)
  3. 按所包含的獨(dú)特子標(biāo)簽選擇: root.find(book[title='Harry Potter']/title)

Mock Server

Mock 即模擬,就是在測(cè)試過程中,對(duì)于某些不容易構(gòu)造或者不容易獲取的對(duì)象,用一個(gè)虛擬的對(duì)象來創(chuàng)建以便測(cè)試的測(cè)試方法,其最大的優(yōu)勢(shì)就是降級(jí)前后端耦合度,使前端工程師可以不依賴后端返回?cái)?shù)據(jù),先開發(fā)前端樣式以及邏輯處理
簡(jiǎn)單來說: Mock是用了解決依賴問題的,將復(fù)雜的/不穩(wěn)定的/還未建立的依賴對(duì)象用一個(gè)簡(jiǎn)單的假對(duì)象來代替

Mock Server 即Mock接口服務(wù)器,可以通過配置快速M(fèi)ock出新的接口
Mock Server的使用范圍

  • 前后端分離項(xiàng)目
  • 所測(cè)接口依賴第三方系統(tǒng)(還未具備)
  • 所測(cè)接口依賴復(fù)雜或依賴的接口不穩(wěn)定,并不作為主要驗(yàn)證對(duì)象

同時(shí)在接口還未開發(fā)好時(shí),提供Mock接口(假接口)會(huì)比只有接口文檔更直觀,并能有效減少溝通成本和一些文檔理解bug

Postman的Mock Server功能

New -> Mock Server

添加接口及返回值
新建Mock環(huán)境
測(cè)試Mock接口

Postman還可以基于Collection建立Mock Server,這里不再詳述

Python+Flask自己搭建Mock接口
使用Flask包我們可以快速搭建Mock接口

pip install flask

from flask import Flask, request, jsonify, abort
import random

app = Flask(__name__) # 實(shí)例化一個(gè)Flask對(duì)象

@app.route("/api/user/reg/", methods=["POST"])
def reg():
    if not request.json or not 'name' in request.json or not 'password' in request.json:
        abort(404) 
    res = [
              {
                "code": "100000",
                "msg": "成功",
                "data": {
                  "name": "李六",
                  "password": "e10adc3949ba59abbe56e057f20f883e"
                }
              },
              {
                "code": "100001",
                "msg": "失敗,用戶已存在",
                "data": {
                  "name": "李六",
                  "password": "e10adc3949ba59abbe56e057f20f883e"
                }
              },
              {
                "code": "100002",
                "msg": "失敗,添加用戶失敗",
                "data": {
                  "name": "李六",
                  "password": "e10adc3949ba59abbe56e057f20f883e"
                }
              }
          ]

    return jsonify(random.choice(res))

if __name__ == '__main__':
    app.run()
使用Mock接口
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • mock簡(jiǎn)介 mock原是python的第三方庫(kù),python 2可以直接安裝mock模塊,但在pytho...
    金融測(cè)試民工閱讀 1,616評(píng)論 0 1
  • 最鎮(zhèn)定自若的我是見到你的時(shí)候。 用盡力氣地洋裝一次最陌生的路過。此后最努力的事情里多了一份祈禱:天涯的你,希望安好...
    瓜蟲之秋閱讀 260評(píng)論 0 0
  • 不知道什么時(shí)候開始,微信已經(jīng)是手機(jī)里面和撥號(hào)短信放在同一界面的APP。朋友圈自然也成為了每個(gè)三分鐘刷一次,每隔十分...
    醉酒少年閱讀 322評(píng)論 0 2
  • 期末考試之前在圖書館奮戰(zhàn)學(xué)習(xí),卻遇到了一個(gè)讓人氣憤的事。 我和朋友在三樓搭乘電梯下樓,在二樓被圖書管理員攔下來,要...
    碎沫閱讀 489評(píng)論 5 0

友情鏈接更多精彩內(nèi)容