接口自動化之斷言

一、接口響應斷言

(一)接口斷言使用場景

  • 問題:
    1. 如何確保請求可以發(fā)送成功。
    2. 如何保證符合業(yè)務需求。
  • 解決方案:
    • 通過獲取響應信息,驗證接口請求是否成功,是否符合業(yè)務需求。

Requests 中的響應結果對象

import requests
from requests import Response

# Response就是一個響應對象
r: Response = requests.get('http://www.example.com')

響應結果類型

屬性 含義
r 響應 Response 對象(可以使用任意的變量名)
r.status_code HTTP 響應狀態(tài)碼
r.headers 返回一個字典,包含響應頭的所有信息。
r.text 返回響應的內容,是一個字符串。
r.url 編碼之后的請求的 url
r.content 返回響應的內容,是一個字節(jié)流。
r.raw 響應的原始內容
r.json() 如果響應的內容是 JSON 格式,可以使用該方法將其解析成 Python 對象。
# 導入依賴
import requests

def test_res_assert():
    # 定義接口的 url 和 json 格式請求體
    url = "https://httpbin.ceshiren.com/get"
    # 發(fā)出 GET 請求,r 接收接口響應
    r = requests.post(url)

響應狀態(tài)碼斷言

  • 基礎斷言:
    • r.status_code
import requests

def test_req():
    r = requests.get("https://httpbin.ceshiren.com/get")
    assert r.status_code == 200

二、JSON 響應體斷言

1、什么是 JSON 響應體

  • JSON格式的響應體指的是HTTP響應中的消息體(message body),它是以JSON格式編碼的數(shù)據(jù)。
{
  "name": "John",
  "age": 30,
  "city": "New York"
}

2、斷言 JSON 格式響應體使用場景

  • 驗證API接口的返回結果是否符合預期。
    • 業(yè)務場景上是否符合預期。
    • 格式是否符合文檔規(guī)范。


3、斷言 JSON 格式響應體

  • r.json():返回 python 字典。
import requests

def test_res_json():
    r = requests.get("https://httpbin.ceshiren.com/get")
    assert r.status_code == 200
    assert r.json()["url"] == "https://httpbin.ceshiren.com/get"

若碰到復雜斷言應該如何處理?

  • 多層嵌套的數(shù)據(jù)提取與斷言: JSONPath
  • 整體結構響應斷言: JSONSchema
  • 自行編寫解析算法

三、整體結構響應斷言

1、響應信息數(shù)據(jù)極為龐大

https://ceshiren.com/t/topic/16658.json

2、針對于“大響應數(shù)據(jù)”如何斷言

  • 針對主要且少量的業(yè)務字段斷言。
  • 其他字段不做數(shù)據(jù)正確性斷言,只做類型與整體結構的校驗。
  • 與前面的版本進行 diff,對比差異化的地方。

3、JSONSchema 簡介

  • 使用 JSON 格式編寫的
  • 可以用來定義校驗 JSON 數(shù)據(jù)的結構
  • 可以用來校驗 JSON 數(shù)據(jù)的一致性
  • 可以用來校驗 API 接口請求和響應

4、JSONSchema 整體結構響應斷言

  1. 預先生成對應結構的 Schema。
  2. 將實際獲取到的響應與生成的 Schema 進行對比。

5、JSONSchema 的生成

  • 通過界面工具生成。
  • 通過第三方庫生成。
  • 通過命令行工具生成。

6、JSONSchema 的生成效果

// # 預期的 JSON 文檔結構
{
  "name": "Hogwarts",
  "Courses": ["Mock", "Docker"]
}
// jsonschema
{
  "$schema": "http://json-schema.org/draft-06/schema#",
  "$ref": "#/definitions/Welcome",
  "definitions": {
    "Welcome": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string"
        },
        "Courses": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "required": ["Courses", "name"],
      "title": "Welcome"
    }
  }
}

7、界面工具生成

  • 復制 JSON 數(shù)據(jù)
  • 粘貼到在線生成工具中
  • 自動生成 JSON Schema 數(shù)據(jù)

JSON Schema 在線生成工具:https://app.quicktype.io

8、第三方庫生成(Python)

  1. 安裝:pip install genson。
  2. 調用方法生成對應的 JSONSchema 數(shù)據(jù)結構。
from genson import SchemaBuilder
def generate_jsonschema(obj):
    # 實例化jsonschem
    builder = SchemaBuilder()
    # 傳入被轉換的對象 
    builder.add_object(obj)
    # 轉換成 schema 數(shù)據(jù)
    return builder.to_schema()

9、JSONSchema 驗證(Python)

  1. 安裝:pip install jsonschema。
  2. 調用 validate() 進行驗證。
def schema_validate(obj, schema):
    '''
    對比 python 對象與生成的 JSONSchame 的結構是否一致
    '''
    try:
        validate(instance=obj, schema=schema)
        return True
    except Exception as e:
        return False

10、JSONSchema 二次封裝

  • 生成JSONSchema
  • 驗證JSONSchema
class JSONSchemaUtils:
    @classmethod
    def generate_schema(cls, obj):
        # 實例化jsonschem
        builder = SchemaBuilder()
        # 傳入被轉換的對象 
        builder.add_object(obj)
        # 轉換成 schema 數(shù)據(jù)
        return builder.to_schema()

    @classmethod
    def schema_validate(cls, obj, schema):
        '''
        對比 python 對象與生成的 json schame 的結構是否一致
        '''
        try:
            validate(instance=obj, schema=schema)
            return True
        except Exception as e:
            return False

四、數(shù)據(jù)庫操作與斷言

1、接口測試響應驗證

如何在測試過程中驗證接口沒有 Bug?

  1. 通過接口響應值
  2. 通過查詢數(shù)據(jù)庫信息輔助驗證

2、接口測試數(shù)據(jù)清理

自動化測試過程中,會產(chǎn)生大量的臟數(shù)據(jù),如何處理?

  • 通過 Delete 接口刪除
  • 自動化測試使用干凈的測試環(huán)境,每次自動化測試執(zhí)行完成之前或之后做數(shù)據(jù)還原。

3、數(shù)據(jù)庫操作注意事項

直接對數(shù)據(jù)庫做查詢之外的操作是非常危險的行為

  • 權限管理嚴格的公司數(shù)據(jù)庫權限給的非常低
  • 表結構復雜,隨便刪除數(shù)據(jù)會影響測試,甚至會導致系統(tǒng)出現(xiàn)異常

4、接口自動化測試常用的數(shù)據(jù)庫操作

  • 連接與配置
  • 查詢數(shù)據(jù)與斷言

Python技術棧:章節(jié)《常用第三方庫pymsql》;Java技術棧:《常用標準庫:數(shù)據(jù)庫操作-JDBC》

實戰(zhàn)目標

  • 第一次全流程實戰(zhàn)的斷言通過數(shù)據(jù)庫驗證

數(shù)據(jù)庫信息

  • 主機: litemall.hogwarts.ceshiren.com
  • 端口: 13306
  • 用戶名: test
  • 密碼: test123456

注意:只有查詢權限

數(shù)據(jù)庫封裝(Python)

  • 封裝數(shù)據(jù)庫配置
  • 封裝 sql 查詢操作
  • 調用方法執(zhí)行 sql 語句
import pymysql

# 封裝建立連接的對象
def get_conn():
    conn = pymysql.connect(
        host="litemall.hogwarts.ceshiren.com",
        port=13306,
        user="test",
        password="test123456",
        database="litemall",
        charset="utf8mb4"
    )
    return conn
# 執(zhí)行sql語句
def execute_sql(sql):
    connect = get_conn()
    cursor = connect.cursor()
    cursor.execute(sql)  # 執(zhí)行SQL
    record = cursor.fetchone()  # 查詢記錄
    return record

if __name__ == '__main__':
    # 執(zhí)行sql語句查詢user123這個用戶的購物車有一個名稱為 hogwarts1 的商品
    execute_sql("select * from litemall_cart where "
                "user_id=1 and deleted=0 and "
                "goods_name='hogwarts1'")

查詢數(shù)據(jù)與數(shù)據(jù)庫斷言(Python)

  • 查詢數(shù)據(jù),添加查詢條件
  • 斷言結果不為 None
# 查詢查詢user123這個用戶的購物車有一個名稱為 hogwarts1 的商品
sql_res = execute_sql("select * from litemall_cart where "
            "user_id=1 and deleted=0 and "
            "goods_name='hogwarts1'")

assert sql_res

五、接口鑒權的多種情況與解決方案

1、接口鑒權是什么

  • 身份認證


    image.png

    image.png

接口鑒權通用的解決方案

  • 認證信息的獲取
  • 認證信息的攜帶
@startuml
scale 800
if (登錄成功?) then
  #pink:響應錯誤;
  detach
endif
#palegreen:響應認證信息;
#palegreen:攜帶認證信息發(fā)起其他請求;
@enduml

后端接口鑒權常用方法

@startmindmap
* 常用方式
** cookie
*** 1\. 攜帶身份信息請求認證
*** 2\. 之后的每次請求都攜帶cookie信息,cookie記錄在請求頭中
** token
*** 1\. 攜帶身份信息請求認證
*** 2\. 之后的每次請求都攜帶token認證信息
*** 3\. 可能記錄在請求頭,可能記錄在url參數(shù)中
** auth
*** 每次請求攜帶用戶的username和password,并對其信息加密
** oauth2(選修)
*** 1\. 攜帶身份信息請求認證
*** 2\. 服務端向指定回調地址回傳code
*** 3\. 通過code獲取token
*** 4\. 之后的請求信息都攜帶token。
*** 典型產(chǎn)品 微信自動化測試
@endmindmap

cookie 鑒權

  1. cookie 的獲取(根據(jù)接口文檔獲?。?/li>
  2. 發(fā)送攜帶 cookie 的請求
    • 直接通過 cookies 參數(shù)
    • 通過 Session() 對象
import requests

class TestVerify:
    def setup_class(self):
        self.proxy = {"http": "http://127.0.0.1:8080",
                      "https": "http://127.0.0.1:8080"}

    def test_cookies_by_write(self):
        # 簡單場景,直接寫入cookie
        url = "https://httpbin.ceshiren.com/cookies"
        requests.get(url, proxies=self.proxy, verify=False, cookies={"hogwarts": "ad"})

    def test_cookies(self):
        # 獲取session 的實例,需要通過Session()保持會話,
        # 即為認證之后,之后所有的實例都會攜帶cookie
        # 可以模仿用戶在瀏覽器的操作
        req = requests.Session()
        # 第一次登陸,植入cookie
        set_url = "https://httpbin.ceshiren.com/cookies/set/hogwarts/ad"
        req.get(set_url, proxies=self.proxy, verify=False)
        # 第二次請求的時候即可攜帶cookie信息
        url = "https://httpbin.ceshiren.com/cookies"
        req.get(url, proxies=self.proxy, verify=False)

token 鑒權

  1. token 的獲?。ǜ鶕?jù)接口文檔獲?。?/li>
  2. 發(fā)送攜帶 token 的請求(根據(jù)接口文檔獲?。?/li>
class TestVerify:
    def setup_class(self):
        self.proxy = {"http": "http://127.0.0.1:8080",
                      "https": "http://127.0.0.1:8080"}

    def test_token(self):
        # 1\. 獲取token
        url = "http://litemall.hogwarts.ceshiren.com/admin/auth/login"
        user_data = {"username": "admin123", "password": "admin123", "code": ""}
        r = requests.post(url, json=user_data, proxies=self.proxy, verify=False, )
        self.token = r.json()["data"]["token"]
        # 2\. 之后的請求均攜帶token
        goods_list_url = "http://litemall.hogwarts.ceshiren.com/admin/goods/list"
        goods_data = {"name": "hogwarts", "order": "desc", "sort": "add_time"}
        r = requests.get(goods_list_url, params=goods_data,
                         headers={"X-Litemall-Admin-Token": self.token},
                         proxies=self.proxy, verify=False)

auth 鑒權(了解即可)

  • 在基本 HTTP 身份驗證中,請求包含格式為 的標頭字段Authorization: Basic<credentials style="box-sizing: inherit;"></credentials>
  • 其中credentials是 ID 和密碼的Base64編碼,由單個冒號連接:。

[圖片上傳失敗...(image-7c678-1694090124455)]

auth 鑒權-代碼示例

import requests
from requests.auth import HTTPBasicAuth

class TestVerify:
    def setup_class(self):
        self.proxy = {"http": "http://127.0.0.1:8080",
                      "https": "http://127.0.0.1:8080"}
    def test_basic_auth(self):
        # 表示訪問一個需要BasicAuth認證的路徑
        # username=用戶名,password=密碼
        # 如果不使用basic auth 則會失敗
        r = requests.get("https://httpbin.ceshiren.com/basic-auth/username/password",
                         proxies=self.proxy, verify=False,
                         auth=HTTPBasicAuth("username", "password"))
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容