在之前的文章裡我們已經學習了Python,unitTest,pyTest,Allure等自動化框架
在這篇文章中我們將藉助這些自動化框架和實際案例來講解如何針對介面進行自動化處理
我們這篇文章將從以下角度展開講解:
首先我們需要了解一個在介面自動化中經常使用的工具類
我們在使用介面自動化時一定會呼叫到不同型別的介面,而Requests庫可以幫助我們快速呼叫介面:
我們需要了解到Requests庫是第三方庫,所以我們需要下載後使用:
# 我們直接在Terminal裡採用pip指令下載Requests庫即可
pip install requests
# 我們可以採用show命令檢視是否安裝成功
pip show requests
我們通常會使用到的關於Requests的方法主要包含以下五種:
# 首先我們注意我們需要匯入Requests庫才可以使用
# Get
Requests.get()
# Post
Requests.post()
# Put
Requests.put()
# Delete
Requests.delete()
下面我們針對Requests庫的使用來做基本的講解:
下面我們首先針對Requests庫的傳送請求進行講解:
# 首先我們需要導包使用
import requests
# 在正式開始講解之前我們需要了解Requests所使用的方法中的一些引數
# url:請求路徑
# params:get特有的引數賦值方法,意思是在url的?之後的引數賦值
# json:通用的引數賦值方法,可以用於複雜的格式也可以用於字典格式
# data:請求體為form表單引數,通常用於字典型別的比較簡單的格式
# header:請求頭引數,通常是Authorization驗證登入資訊或者引數傳輸格式資訊等
# files:檔案格式,使用字典傳輸,且字典中含有'file'鍵名稱
# 首先我們先來看一下常用的Get請求
# Requests.get(url,params/json)
r = requests.get('http://httpbin.org/get')
# 如果我們想要新增引數進行傳輸,我們可以直接在url後面新增?+引數資訊等
r = requests.get('http://httpbin.org/get?name=germey&age=20')
# 我們也可以採用params引數或者json引數進行傳輸
data = {
'name':'germey',
'age':22
}
r = requests.get('http://httpbin.org/get',params=data)
r = requests.get('http://httpbin.org/get',json=data)
# 下面我們瞭解一下Post請求,Put和Delete操作和Post操作完全相同,這裡不做介紹
# Requests.get(url,json)
r = requests.post('http://httpbin.org/post')
# 和Get方法不同的是,Post不能採用params也不能採用url拼接,只能採用json進行傳輸
data = {
"name":"germey",
"age":"22"
}
r = requests.post('http://httpbin.org/post',data=data)
# 除了基本的url和json之外,我們在請求過程中還需要很多引數型別
# 例如header頭,我們的大部分操作都需要做登陸驗證,而登入一般會含有一個Authorization屬性,後面value值為Token值,這裡我們講述
# 但是除了Authorization屬性外,我們還可能會傳輸引數型別Content-Type等其他資訊
# 我們通常將header寫為字典格式進行傳輸
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0',
'my-test':'Hello'
}
r = requests.get('http://httpbin.org/get',headers=headers)
# 我們在傳輸過程中也經常傳輸檔案型別,我們通常需要讀取檔案並將其儲存為Python中的檔案才可以使用
# 我們需要注意以二進位制方式讀取檔案
# 我們在post方法中需要傳輸字典,字典key值為'file'是預設名詞
files = {'file':open('favicon.ico','rb')}
# 我們呼叫post方法並使用files引數進行檔案上傳
r = requests.post('http://httpbin.org/post',files=files)
# 我們還需要注意一些特殊的網站,如果該網站的證書沒有被CA機構信任,程式將會出錯,提示SSL證書驗證錯誤
# 那麼我們在存取時,只需要將verify引數設定為False即可
r = requests.get('https://www.12306.cn',verify=False)
# 針對特別防範的網站,當我們大量進行資料爬取或者重複使用時,可能會觸發該網站的自衛系統將我們當前IP進行遮蔽
# 我們可以通過使用代理來防止這種情況(IP可以在網上購買)
proxies = {
'http': 'http://161.35.4.201:80',
'https': 'https://161.35.4.201:80'
}
r = requests.get('http://httpbin.org/get', proxies=proxies)
在講述完Requests庫的傳送請求後,我們可以針對Requests請求後的Response響應進行講述:
# 我們在傳送請求後伺服器會對我們的請求進行響應,這就是所獲得的Response,所有請求都會獲取到該響應
# 我們下面來介紹一些我們常用的response型別的方法
# 獲取狀態碼
response.status_code
# JSON形式的響應內容
response.json()
# 文字形式的響應內容
response.text
# 請求url
response.url
# 檢視響應頭部字元編碼
response.encoding
# 頭資訊
response.headers
# cookie資訊
response.cookies
# 我們通常獲得這些資訊之後可以進行檢視,我們可以通過print直接列印檢視
print(response.status_code)
# 但更多情況下,我們通過這些資訊來做斷言判斷是否符合我們預期
assert 200 == response.status_code
assert "成功" in response.text
# 當然我們在必要情況下我們還需要獲取到對應的值去使用
# 例如我們通常需要一個Token來表示我們處於登陸狀態,那麼我們就需要在執行登陸操作時將token值獲取並儲存下來
self.token = r.json().get("token")
# 如果我們需要cookie,我們也可以直接獲取cookies並將其儲存在一個列表中
response = requests.get("百度一下,你就知道")
for key, value in response.cookies.items():
print(key + '=' + value)
下面我們來講解介面自動化的一些基礎資訊
既然我們要學習介面自動化,那麼我們肯定首先需要了解介面測試:
那麼我們的介面測試主要針對哪些方面:
我們測試人員針對介面測試的主要時間段是在哪裡:
開發進行系統測試提測前,可以先進行介面自動化指令碼的編寫
開發進行系統測試提測後,優先進行系統測試用例的執行,再進行介面自動化指令碼的編寫
所以我們介面測試的主要時間段針對於前後端聯調的時間進行介面測試
下面我們來講解介面測試的基本操作順序:
# 1.選取自動化測試用例
# 我們一般會在前後端開發時進行測試用例的書寫,我們僅需要選取我們所測試模組的測試用例即可
# 2.搭建自動化測試環境
# 這個主要根據公司本身,一般由主管進行搭建,員工拉取書寫更新上傳即可
# 3.搭建自動化測試框架
# 這個主要也是公司進行選取,不過一般都是python+pytest+allure基本框架加上一些輔助框架
# 4.程式碼實現自動化
# 這個就是我們介面測試自動化主要書寫的部分
# 5.輸出測試報告
# 我們一般會採用allure進行美觀測試報告生成
# 6.實現持續整合
# 我們介面測試的目的其實是為了在本次開發時使當前開發不影響之前功能的使用,所以我們的介面自動化需要保證效率重複使用等多方面
我們在進行介面測試時可以通過三種方式獲得介面:
在測試頁面使用F12檢視呼叫介面資訊
採用Charles等抓包工具檢視介面資訊
向專案經理/後端人員索要介面檔案資訊
最後我們稍微提一下如果我們使用手動介面測試,我們一般會使用Postman工具進行測試,我們這裡不做詳解
接下來我們通過一個介面自動化範例來講解介面自動化
我們在接觸介面自動化程式碼之前,我們首先需要了解這個框架主要需要什麼:
# 首先假設我們建立了一個Pro_viv檔案來當作主目錄
# 首先我們需要一個api資料夾
# 我們會將我們所需要呼叫的Api介面進行封裝(Requests庫相關的資訊)
# 然後我們需要一個script資料夾
# 我們需要將我們所使用的case進行儲存,其實就是我們pytest中所使用的測試用例,我們的資料呼叫也是在這裡進行呼叫
# 然後我們需要一個data資料夾
# 我們會將我們所需要使用的jpg或json等資訊檔案儲存進該資料夾
# 最後我們還需要一個report資料夾
# 我們會將所生成的allure報告檔案存放在該資料夾下
# 除了資料夾外,我們還需要兩個檔案來進行一些資訊整理
# 首先我們需要一個config.py
# 我們會使用該檔案來存放一些通用的路徑或其他資訊
# 我們還需要一個pytest.ini
# 這是pytest的組態檔,我們可以在裡面修改pytest相關的資訊
那麼下面我們將通過多個需求案例來逐步瞭解介面自動化的使用
我們首先給出需求資訊:
# 地址:http://kdtx-test.itheima.net/api/login
# 方法:Post
# 請求資料:
# 請求頭:Content-Type: application/json
# 請求體:{"username":」admin", "password": " admin123","code":"2", "uuid":"驗證碼介面返回資料"}
我們首先需要書寫介面,所以我們需要在api檔案下建立login.py進行介面書寫:
# api 資料夾 - login.py
# 介面封裝時,重點是依據介面檔案封裝介面資訊
# 需要使用的測試資料是從測試用例傳遞的、介面方法被呼叫時需要返回對應的響應結果
# 所以我們在書寫完介面方法後,我們還需要到script資料夾下建立test檔案來建立case來執行pytest
# 導包
import requests
# 建立介面類
class LoginAPI:
# 初始化
def __init__(self):
# 指定url基本資訊
self.url_verify = "http://kdtx-test.itheima.net/api/captchaImage"
self.url_login = "http://kdtx-test.itheima.net/api/login"
# 驗證碼
def get_verify_code(self):
return requests.get(url=self.url_verify)
# 登入
# 我們在登入時需要請求資訊,我們這裡新增一個引數,當我們使用case時傳參並呼叫
def login(self, test_data):
return requests.post(url=self.url_login, json=test_data)
當我們書寫完介面資訊之後,我們就可以到script資料夾下建立test檔案進行測試用例書寫:
# script 資料夾 - verify.py
Class TestLogin:
uuidValue = None
# 驗證碼測試
def test01_get_verify_code1(self):
# 獲取驗證碼
res_v = self.login_api.get_verify_code()
# 登入驗證
def test02_login1(self):
# 這裡我們直接將資訊寫為列表進行賦值處置
login_data = {
"username": "admin",
"password": "admin123",
"code": "2",
"uuid": "f3334fd726bf4155b787198c701786b6"
}
res_l = self.login_api.login(test_data=login_data)
# 驗證碼測試2
def test03_get_verify_code1(self):
# 獲取驗證碼
res_v = self.login_api.get_verify_code()
# 一些資訊的列印
print(res_v.status_code)
print(res_v.json())
# 列印uuid資料,這是我們在登入時所需要使用的資料
print(res_v.json().get("uuid"))
# 我們可以採用一個類變數來將uuid儲存起來進行使用
self.uuidValue = res_v.json().get("uuid")
# 登陸驗證2
def test04_login(self):
# 由於我們的uuid是通過get_verify_code獲得的驗證碼,我們可以通過pytest連續執行兩條case並獲取uuid進行使用
login_data = {
"username": "admin",
"password": "admin123",
"code": "2",
"uuid": self.uuidValue
}
res_l = self.login_api.login(test_data=login_data)
我們首先給出需求資訊:
# 課程模組介面封裝:核心在於依據介面檔案實現介面資訊封裝、重點關注介面如何被呼叫
# 介面資訊:
# URL:http://kdtx-test.itheima.net/api/clues/course
# 方法:Post
# 請求資料:
# 請求頭:{ "Content-Type ": "application/json ", "Authorization": "xxx " }
# 請求體:{ "name": "測試開發提升課01", "subject": "6","price": 899,"applicablePerson": "2", "info": "測試開發提升課01"}
我們首先需要書寫介面,所以我們需要在api檔案下建立course.py進行介面書寫:
# api 資料夾 - course.py
# 我們將在該介面類中實現多個介面,我們將書寫除了上述需求資訊外的其他介面
# 導包
import requests
# 建立介面類
class CourseAPI:
# 初始化
def __init__(self):
self.url_add_course = "http://kdtx-test.itheima.net/api/clues/course"
self.url_select_course = "http://kdtx-test.itheima.net/api/clues/course/list"
# 我們需要注意:
# 我們合同的操作均需要在登入前提下進行操作
# 而我們判斷是否登入的條件就是在headers請求頭中是否存在符合要求的token
# 所以我們下述的介面方法中,除了我們所需要的資料資訊外,我們還需要新增headers,並提供Authorization資訊
# 課程新增
def add_course(self, test_data, token):
return requests.post(url=self.url_add_course, json=test_data, headers={"Authorization": token})
# 查詢課程列表
def select_course(self, test_data, token):
# 這裡我們可以採用url拼接,也可以採用params來傳遞引數,
# return requests.get(url=self.url_select_course,params=params,headers={"Authorization": token})
return requests.get(url=self.url_select_course + f"/{test_data}", headers={"Authorization": token})
# 修改課程
def update_course(self, test_data, token):
return requests.put(url=self.url_add_course, json=test_data, headers={"Authorization": token})
# 刪除課程
def delete_course(self, course_id, token):
return requests.delete(url=self.url_add_course + f"/{course_id}", headers={"Authorization": token})
當我們書寫完介面資訊之後,我們就可以到script資料夾下建立test檔案進行測試用例書寫:
# 導包
import config
from api.login import LoginAPI
from api.course import CourseAPI
from api.contract import ContractAPI
# 建立測試類
class TestContractBusiness:
# 初始化(由於我們需要使用登入時的Token,所以我們進行儲存)
token = None
# 前置處理
def setup(self):
# 範例化介面物件
self.login_api = LoginAPI()
self.course_api = CourseAPI()
self.contract_api = ContractAPI()
# 後置處理
def teardown(self):
pass
# 1、登入成功
def test01_login_success(self):
# 獲取驗證碼
res_v = self.login_api.get_verify_code()
# 登入
login_data = {
"username": "admin",
"password": "admin123",
"code": "2",
"uuid": res_v.json().get("uuid")
}
res_l = self.login_api.login(test_data=login_data)
# 提取登入成功之後的token資料並儲存在類的屬性中
TestContractBusiness.token = res_l.json().get("token")
# 2、課程新增成功
def test02_add_course(self):
add_data = {
"name": "測試開發提升課01",
"subject": "6",
"price": 899,
"applicablePerson": "2",
"info": "測試開發提升課01"
}
response = self.course_api.add_course(test_data=add_data, token=TestContractBusiness.token)
print(response.json())
我們首先給出需求資訊:
# 請求頭:{ "Content-Type ": " multipart/form-data ", "Authorization": "xxx " }
# 請求體:{" file " : 合同檔案"}
# 介面資訊:
# 新增合同:
# 地址:http://kdtx-test.itheima.net/api/contract
# 方法:Post
# 請求資料:
# 請求頭:{ "Content-Type ": "application/json ", "Authorization": "xxx " }
# 請求體:{ "name": "測試888", "phone": "13612345678", "contractNo": "HT10012003", "subject": "6", "courseId": " 99", "channel": "0", "activityId": 77, "fileName": "xxx"}
我們首先需要書寫介面,所以我們需要在api檔案下建立contract.py進行介面書寫:
# 導包
import requests
# 建立介面類
class ContractAPI:
# 初始化
def __init__(self):
self.url_upload = "http://kdtx-test.itheima.net/api/common/upload"
self.add_contrat = "http://kdtx-test.itheima.net/api/contract"
# 合同上傳介面
def upload_contract(self, test_data, token):
# 我們的合同需要一份檔案資訊上傳,所以我們引數這裡使用到了file
# 其實我們的headers頭部的Content-Type資訊也被修改,但由於該資訊是根據傳輸內容更新的,所以我們不需要設定
return requests.post(url=self.url_upload, files={"file": test_data}, headers={"Authorization": token})
# 合同新增
def add_contract(self, test_data, token):
# 這裡就是很正常的新增
return requests.post(url=self.add_contrat, json=test_data, headers={"Authorization": token})
當我們書寫完介面資訊之後,我們就可以到script資料夾下建立test檔案進行測試用例書寫:
# 導包
import config
from api.login import LoginAPI
from api.course import CourseAPI
from api.contract import ContractAPI
# 建立測試類
class TestContractBusiness:
# 初始化
token = None
# 前置處理
def setup(self):
# 範例化介面物件
self.login_api = LoginAPI()
self.course_api = CourseAPI()
self.contract_api = ContractAPI()
# 後置處理
def teardown(self):
pass
# 1、登入成功
def test01_login_success(self):
# 獲取驗證碼
res_v = self.login_api.get_verify_code()
# 登入
login_data = {
"username": "admin",
"password": "admin123",
"code": "2",
"uuid": res_v.json().get("uuid")
}
res_l = self.login_api.login(test_data=login_data)
# 提取登入成功之後的token資料並儲存在類的屬性中
TestContractBusiness.token = res_l.json().get("token")
# 2、上傳合同成功
def test02_upload_contract(self):
# 這裡我們需要將對應的檔案採用二進位制讀取並且轉化為file型別將其作為引數傳參
f = open(config.BASE_PATH + "/data/test.pdf", "rb")
# 這裡的test_data在Api中作為files的file被傳遞
response = self.contract_api.upload_contract(test_data=f, token=TestContractBusiness.token)
print(response.json())
# 3、合同新增成功
def test03_add_contract(self):
# contractNo: 資料唯一
add_data = { "name": "測試888", "phone": "13612345678", "contractNo": "HT20230007", "subject": "6", "courseId": " 99", "channel": "0", "activityId": 77, "fileName": "xxx"}
response = self.contract_api.add_contract(test_data=add_data, token=TestContractBusiness.token)
print(response.json())
下面我們來講解針對於單個介面進行多測試用例測試的方法:
# 我們這裡以Login的API為基準
# 我們需要採用不同的測試用例來進行測試並判斷是否滿足需求
我們首先給出測試用例圖表:
然後我們採用程式碼進行介面自動化測試用例書寫:
# 導包
from api.login import LoginAPI
# 建立測試類
class TestLoginAPI:
# 初始化
uuid = None
# 前置處理
# 我們將uuid驗證碼作為所有case方法執行前的前置操作進行執行並儲存使用
def setup(self):
# 範例化介面類
self.login_api = LoginAPI()
# 獲取驗證碼
response = self.login_api.get_verify_code()
# 提取驗證碼介面返回的uuid引數值
TestLoginAPI.uuid = response.json().get("uuid")
# 後置處理
def teardown(self):
pass
# 下面我們通過多個Case的書寫來模擬多場景情況
# 我們下面Case的基本邏輯沒有發生修改,僅僅是針對引數進行了處理
# 登入成功
def test01_success(self):
login_data = {
"username": "manager",
"password": "123456",
"code": "2",
"uuid": TestLoginAPI.uuid
}
response = self.login_api.login(test_data=login_data)
# 我們使用斷言來判斷是否符合需求,若符合需求pytest會通過案例,若不符合會進行報錯
assert 200 == response.status_code # 斷言響應狀態碼為200
assert '成功' in response.text # 斷言響應資料包含'成功'
assert 200 == response.json().get("code") # 斷言響應json資料中code值
# 登入失敗(使用者名稱為空)
def test02_without_username(self):
login_data = {
"username": "",
"password": "123456",
"code": "2",
"uuid": TestLoginAPI.uuid
}
response = self.login_api.login(test_data=login_data)
# 我們使用斷言來判斷是否符合需求,若符合需求pytest會通過案例,若不符合會進行報錯
assert 200 == response.status_code # 斷言響應狀態碼為200
assert '成功' in response.text # 斷言響應資料包含'成功'
assert 200 == response.json().get("code") # 斷言響應json資料中code值
# 登入失敗(未註冊使用者)
def test03_username_not_exist(self):
login_data = {
"username": "jack666",
"password": "123456",
"code": "2",
"uuid": TestLoginAPI.uuid
}
response = self.login_api.login(test_data=login_data)
# 我們使用斷言來判斷是否符合需求,若符合需求pytest會通過案例,若不符合會進行報錯
assert 200 == response.status_code # 斷言響應狀態碼為200
assert '成功' in response.text # 斷言響應資料包含'成功'
assert 200 == response.json().get("code") # 斷言響應json資料中code值
我們在前面通過書寫多個Case來進行同一介面的自動化測試,但這種方法會導致程式碼冗餘
所以我們通常會通過pytest的引數化設定來統一資料將其放入Case用例中按順序依次執行來減少程式碼冗餘:
# 我們可以將資料單獨存放在檔案頂層
# 我們所傳入的資料需要採用列表作為外層,採用字典作為資料引數進行儲存
# 導包
from api.login import LoginAPI
import pytest
import json
# 測試資料
test_data = [
("manager", "123456", 200, '成功', 200),
("", "123456", 200, '錯誤', 500),
("jack666", "123456", 200, '錯誤', 500),
]
class TestLoginAPI:
# 初始化
uuid = None
# 前置處理
def setup(self):
# 範例化介面類
self.login_api = LoginAPI()
# 獲取驗證碼
response = self.login_api.get_verify_code()
# 提取驗證碼介面返回的uuid引數值
TestLoginAPI.uuid = response.json().get("uuid")
# 後置處理
def teardown(self):
pass
# 多Case登入驗證
# 這裡我們使用了我們上面所定義的test_data作為資料來源來進行引數化處理
# 我們可以將我們所需要更替的數值全部採用引數化處理,使處理資料變得多樣化
# 這裡所使用的parametrize是pytest的內容,我們在上篇文章已經講過,如果不瞭解可以去檢視
@pytest.mark.parametrize("username, password, status, message, code",test_data)
def test01_success(self, username, password, status, message, code):
# 這裡的username,password使用引數化
login_data = {
"username": username,
"password": password,
"code": "2",
"uuid": TestLoginAPI.uuid
}
response = self.login_api.login(test_data=login_data)
# 這裡的斷言判斷均使用引數化,因為不同條件下所產生的response資料是不同的
assert status == response.status_code
assert message in response.text
assert code == response.json().get("code")
最後我們需要解釋我們在最開始所提及到的兩個介面設定資訊
我們在最開始提及到了在我們的檔案目錄下建立了一個Config.py檔案
下面我們來解釋一下該檔案的主要作用:
# 我們通常在該檔案下定義我們全域性都需要使用的資料資訊,來減少我們程式碼的冗餘
# 例如我們在之前的介面或測試用例中都需要使用到的url,我們可以在這裡定義其專案url字首,然後我們只需要書寫對應的介面路徑即可
# 又或者說由於我們之前的檔案上傳沒有固定的檔案目錄,我們需要從當前檔案下去推斷data資料夾在哪個位置
# 但是如果我們專案發生改動,或者我們借鑑該程式碼去書寫其他部分的檔案,我們就需要去重新推斷data資料夾在哪個位置
# 所以我們可以通過config.py直接獲得絕對路徑下的data目錄的位置,然後我們只需要進行拼接就可以獲得到我們所需要的資料資訊
所以我們的config.py檔案可以這樣去定義:
# config.py檔案
# 導包
import os
# 設定專案環境域名
BASE_URL = "http://kdtx-test.itheima.net"
# 獲取專案根路徑
BASE_PATH = os.path.dirname(__file__)
當然我們對應的介面程式碼和測試用例程式碼也需要進行修改:
# 首先針對API檔案,我們需要去修改我們的url路徑資訊
def __init__(self):
# 指定url基本資訊
# self.url_verify = "http://kdtx-test.itheima.net/api/captchaImage"
self.url_verify = config.BASE_URL + "/api/captchaImage"
# self.url_login = "http://kdtx-test.itheima.net/api/login"
self.url_login = config.BASE_URL + "/api/login"
# 針對檔案上傳時的路徑我們也可以去修改
def test03_upload_contract(self):
# f = open("../data/test.pdf", "rb")
f = open(config.BASE_PATH + "/data/test.pdf", "rb")
response = self.contract_api.upload_contract(test_data=f, token=TestContractBusiness.token)
print(response.json())
我們之前還提到了一個pytest.ini檔案,該檔案其實就是針對pytest執行用例的初始化進行設定:
# 該檔案其實屬於pytest的學習範疇,我們在這裡簡單介紹一下
[pytest]
# 表示我們在Terminal執行pytest時的預設執行設定
# 這裡我們採用-s表示詳情資訊展示,採用--alluredir表示採用allure生成測試報告並生成在report資料夾下
addopts=-s --alluredir report
# 這裡表示我們所需要執行的測試用例的檔案範圍
testpaths=./script
# 這裡規定了設定測試搜尋的模組檔名稱
python_files=test*
# 這裡規定了我們所執行的測試類的類名規範
python_classes=Test*
# 這裡規定了我們所執行的測試方法的方法名規範
python_functions=test*
這篇文章中詳細介紹了以pytest為框架的介面自動化實現的基本資訊,希望能為你帶來幫助
下面給出我學習和書寫該篇文章的一些參考文章,大家也可以去查閱: