1. 前言
大家好,我是Arthur,擁有超過10年以上的銀行測試經驗,目前在一家互聯網創業公司擔任測試經理。在我們那個年代,基本上都是不會寫代碼的做測試工作,而且基本都是純手工;最近幾年,測試開發開始流行,互聯網行業越來越重視測試人員的綜合能力,使得我們這些老測試也必須轉型,才能跟上時代的步伐,因此我也報名了狂師老師的 全棧測開訓練營,之前我也在一些網絡培訓網站上學習一些新的知識點,但我感覺狂師的課程無論是范圍、廣度、深度比大多數培訓機構強很多,也正是工作當中經常能用到的內容,非常值得大家學習。那么,今天我將結合訓練營課程的講解,分享一些關於接口自動化測試學習實踐總結,並且這些我已經應用到了實際工作上,希望對大家也有所幫助。
2. 工作需求背景
- 利用
Pytest+Request+Allure+Jenkins
實現接口自動化; - 實現一套腳本多套環境執行;
- 利用參數化數據驅動模式,實現接口與測試數據分離
- 使用
logger
定制實現自動化測試日志記錄
3. 接口自動化項目代碼編寫(先在window實現)
3.1 項目准備
先在window安裝響應的環境依賴
- 安裝python3.7(要保證pip能用,一般安裝python3.7會自動安裝pip)
- 安裝pytest框架---- pip install pytest
- 安裝request庫---- pip install request
- 安裝openpyxl庫(測試數據保存在excel中,需要依賴讀取excel的庫)---- pip install openpyxl
- 安裝pycharm(編寫python腳本工具)
注意:可能還需要一些依賴的東西,項目步驟里會依據需要進行安裝
3.2 設計基於pytest的測試框架結構
在pycharm中開發構建項目結構
- common:存放公共方法
- config:存放環境配置信息
- lib:存放第三方庫
- main:框架主入口
- report:存放allure測試報告
- test_case:存放測試用例
- test_data:存放測試數據
3.3 實現接口公共請求發送能力
從這一步開始正式編寫代碼
封裝http請求的公共能力(封裝request庫,變成自己的公共處理能力),放到common目錄下。
# encoding: utf-8
# 代碼來源:全棧測開訓練營
import requests
import urllib3
# from urllib3.exceptions import InsecureRequestWarning
urllib3.disable_warnings()
# 加這句不會報錯(requests證書警告)
# requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
class HTTPRequests(object):
def __init__(self, url):
self.url = url
self.req = requests.session()
# 依據自己公司的請求頭默認值配置
self.head = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64; Trident/7.0; rv:11.0) like Gecko',
'Accept': 'image/gif, image/jpeg, image/pjpeg, application/x-ms-application, application/xaml+xml, '
'application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, '
'application/msword, */*',
'Accept-Language': 'zh-CN'}
# 封裝自己的get請求,獲取資源
def get(self, uri='', params='', data='', headers=None, cookies=None, verify=False):
if headers is None:
headers = self.head
# print("請求頭是:{}".format(headers))
url = self.url + uri
response = self.req.get(url, params=params, data=data, headers=headers, cookies=cookies, verify=verify)
return response
# 封裝自己的post請求,獲取資源
def post(self, uri='', params='', data='', headers=None, cookies=None, verify=False):
if headers is None:
headers = self.head
url = self.url + uri
response = self.req.post(url, params=params, data=data, headers=headers, cookies=cookies, verify=verify)
return response
# 封裝自己的put請求,獲取資源
def put(self, uri='', params='', data='', headers=None, cookies=None, verify=False):
if headers is None:
headers = self.head
url = self.url + uri
response = self.req.put(url, params=params, data=data, headers=headers, cookies=cookies, verify=verify)
return response
# 封裝自己的delete請求,獲取資源
def delete(self, uri='', params='', data='', headers=None, cookies=None, verify=False):
if headers is None:
headers = self.head
url = self.url + uri
response = self.req.delete(url, params=params, data=data, headers=headers, cookies=cookies, verify=verify)
return response
3.4 抽離測試環境配置信息
這個步驟的目的有三個
- 為了配置三個不同環境(測試、開發、生產)的URL,每個環境接口測試的URL是不一樣的,設置這樣一個枚舉類,方便后面的程序根據不同的環境,獲取不同環境的URL,里面的URL依據自己公司的地址修改,放到config目錄
- 獲取token需要登錄,這里可以設置一個全局的賬號密碼,這個賬號密碼獲取的token可以給整個接口自動化使用
- 配置獲取token的uri,這個uri三個環境的是一致的,登錄的接口依據環境只是URL不同,URI還是一致的。
# encoding: utf-8
# 代碼來源:全棧測開訓練營
import enum
class URLConf(enum.Enum):
"""環境配置信息"""
url_mapping = {
'dev': 'https://www.dev.com',
'test': 'https://www.test.com',
'prod': 'https://www.prod.com'
}
# token固定的用戶名密碼,固定用"/"分割用戶名和密碼
email_user = {
'dev': 'dev@qq.com',
'test': 'zidonghua@qq.com/96e79218965eb72c92a549dd5a330112',
'prod': 'prod@qq.com'
}
login_uri = r'/api/auth/login/account/v1'
3.5 創建conftest.py放置一些公共的fixture
1、pytest_addoption,設置了只允許輸入dev/test/prod三個參數,以區分測試、開發、生產三個環境
2、get_env的fixture,它的作用是你在命令行執行接口自動化時,可以輸入--env test將對應的環境信息傳入進去
3、http的fixture,這里依據--env test傳入的環境信息,去枚舉類里獲取對應環境的URL,然后返回一個http的session,供測試案例使用
4、get_token_head,依據--env test傳入的環境信息,調用獲取token方法,並將token放置到請求頭head里返回(token一般放在請求頭里,這里依據自己公司的請求,返回對應的token信息就可以了)
# encoding: utf-8
# 代碼來源:全棧測開訓練營
import logging
import os
import pytest
from common.http_request import HTTPRequests
from config.url_config import URLConf
datadir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "test_data")
logger = logging.getLogger('conftest日志')
def pytest_addoption(parser):
# choices 只允許輸入的值的范圍
parser.addoption(
"--env", action="store", default='test', choices=['dev', 'test', 'prod'], help="set env"
)
# 獲取命令行參數的fixture
@pytest.fixture(scope='session')
def get_env(request):
# print("fixutre..................")
return request.config.getoption('--env')
# 聲明一個返回http請求對象的fixture,所有用例在一個session中
# @pytest.fixture(scope='module', autouse=True)
@pytest.fixture(autouse=True)
def http(request):
env = request.getfixturevalue("get_env")
url_mapping = URLConf.url_mapping.value
url = url_mapping.get(f'{env}')
http = HTTPRequests(url)
return http
@pytest.fixture(scope='session')
def get_token_head(request):
env = request.getfixturevalue("get_env")
url_mapping = URLConf.url_mapping.value
url = url_mapping.get(f'{env}')
http = HTTPRequests(url)
user = URLConf.email_user.value
user_list = user.get(f'{env}').split("/")
username = user_list[0]
password = user_list[1]
param = {'clientType': 2,
'language': 'en',
'loginId': username,
'loginPassword': password}
logger.info("請求的url=={}".format(url))
response = http.post(uri=r'/api/auth/login/account/v1', data=param)
logger.info("獲取的返回值是:".format(response.text))
token = None
if response.status_code == 200:
token = response.json().get('result')['token']
else:
token = 'get token fail'
head = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64; Trident/7.0; rv:11.0) like Gecko',
'Accept': 'image/gif, image/jpeg, image/pjpeg, application/x-ms-application, application/xaml+xml, '
'application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, '
'application/msword, */*',
'Accept-Language': 'zh-CN',
'Authorization': token}
yield head
3.6 將測試數據放到excel中
我們的測試數據是放在excel中,注意,這里有prod\test\dev三個目錄,對應三個環境的測試數據,我這里只創建了test測試環境的測試數據。這里的測試數據需要包含兩部分:
- 你調用接口傳入的所有參數;
- 你要斷言的所有信息,因為你傳的參數不同,返回的內容就不同,你斷言的內容也就不相同了。
那么這時候,就需要一個讀取excel的公共方法了,放到common里
# 創建解析excel的方法
import logging
from openpyxl import load_workbook
logger = logging.getLogger("讀取excel")
class ParseExcel(object):
def __init__(self, excelPath, sheetName):
self.wb = load_workbook(excelPath)
self.sheet = self.wb[sheetName]
self.maxRowNum = self.sheet.max_row
# 依據傳入的數字,決定獲取幾列excel數據
def getDataFromSheet(self, num):
dataList = []
for line in self.sheet.rows:
tmplist = []
for i in range(num):
tmplist.append(line[i].value)
dataList.append(tmplist)
print("dadddddd:{}".format(dataList))
return dataList[2:]
這里,還需要再test_data中,創建一個文件,為了獲取前面test_data依據環境創建的dev/test或prod文件目錄
注意:這里只有一個test或prod或dev的文字,是為了拼接....test_data/test目錄獲取對應環境的excel測試數據而使用的,每次環境切換前,需要更改這個文件,可能這並不是一個好方法,如果大家有找到更好的方法,也可以分享一下
3.7 開始編寫自動化測試案例了
測試案例中有幾個點,需要解釋一下:
1、authBaseDir,這個就是根據test_data/test拼接出來的獲取測試數據的目錄
2、allure.feature,在測試報告中,會展現這個接口名稱,這個名稱最好與你公司的開發寫的接口模塊保持一致,方便后續查找問題
3、allure.story 這里也要與開發寫的具體某個接口的名稱保持一致。
4、pytest.mark.parametrize,這里就是運用的DDT數據驅動的模式,從excel中一條一條的獲取數據,然后執行同一條接口測試用例,excel中比如有3條數據,那么就表示這個案例依據每一條數據的參數,總共執行了三次
# encoding: utf-8
"""
create by Arthur
Account Api模塊
"""
import logging
import os
import allure
import pytest
from common.get_data_url import get_data_url
from common.parse_excel import ParseExcel
datadir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "test_data")
data_url = get_data_url()
# 獲取到test_data\test的目錄,如果是prod環境,那么就是獲取test_data\prod目錄
authBaseDir = os.path.join(datadir, data_url)
logger = logging.getLogger("Account Api模塊日志")
@allure.feature("AccountApi模塊")
@pytest.mark.webtest
class TestAccountApi(object):
"""Query Related Achievements: /api/auth/account/achievement/related/query/v1
"""
Query_Related_Achievements_dir = os.path.join(authBaseDir, 'Query_Related_Achievements.xlsx')
logger.info("Query_Related_Achievements測試數據的路徑是:{}".format(Query_Related_Achievements_dir))
parse = ParseExcel(Query_Related_Achievements_dir, 'Sheet1')
Query_Related_Achievements_params = parse.getDataFromSheet(5)
@allure.story("Query Related Achievements(查詢用戶成就信息)")
@pytest.mark.parametrize("clientType,language,retCode,istoken,result", Query_Related_Achievements_params)
def test_001_Query_Related_Achievements(self, get_token_head, http, clientType, language, retCode, istoken, result):
uri = '/api/auth/account/achievement/related/query/v1'
params = {"clientType": clientType, "language": language}
if istoken == 'yes':
header = get_token_head
response = http.get(uri=uri, params=params, headers=header)
json_req = response.json()
logger.info("Query_Related_Achievements有token的返回值是:{}".format(json_req))
assert json_req.get('retCode') == 200
assert json_req.get('result')[0]['smallImg'] == result
else:
response = http.get(uri=uri, params=params)
json_req = response.json()
logger.info("Query_Related_Achievements沒有token的返回值是:{}".format(json_req))
assert json_req.get('retCode') == 401
assert json_req.get('message') == result
3.8 集成allure
寫到這里,是不是發現前面的allure.feature是不是用不了呢?這是因為我們還沒有集成allure進去。
1、下載allure
,放到lib目錄下,使你的工程具備allure的能力。
2、pip install allure-pytest
安裝pytest對應的allure包
3.9 這時候就可以創建一些執行策略了
1、先在main中創建一個pytest.ini文件,設置一些執行參數
2、在main中創建執行策略
- 先在run_pytest方法中,執行案例並生成allure的json格式的報告文件,這里可以帶--env prod將對應環境信息傳入,這里沒有傳是因為默認是test環境,不傳入的話就是執行的test環境測試數據
- general_report方法時將生成的json格式的報告,最終生成html文件放置到report下面的目錄中
- 創建一個線程,先執行run_pytest,再執行general_report,避免json文件沒有生成,這樣生成html文件的報告數據可能不全,甚至沒有。
# encoding: utf-8
# 代碼來源:全棧測開訓練營
"""
所有案例執行並生成allure測試報告的執行策略
"""
import os
import sys
import threading
import pytest
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../')
from common.report import Report
project_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
report_dir = os.path.join(project_root, 'report')
# 存放測試結果的目錄,會生成一推json文件
result_dir = os.path.join(report_dir, 'allure_result')
allure_report = os.path.join(report_dir, 'allure_report')
report = Report()
# 定義搜索條件,搜索所有以test開頭的用例
tag = 'test'
def run_pytest():
# --clean-alluredir
# pytest.main(['-vv', '-s', '-m', 'webtest', f'--alluredir={result_dir}', '--clean-alluredir'])
# 執行前清除allure_result數據,避免生成報告時,會把上次執行的數據帶進去
pytest.main(['-vv', '-s', '-k', f'{tag}', f'--alluredir={result_dir}', '--clean-alluredir'])
def general_report():
# 調用cmd方法 report.allure,根據windows或linux環境判斷
# 然后執行生成報告的方法generate
# --clean 覆蓋路徑,將上次的結果覆蓋掉
cmd = "{} generate {} -o {} --clean".format(report.allure, result_dir, allure_report)
# 執行命令行命令,並通過read()方法將命令的結果返回
print(os.popen(cmd).read())
if __name__ == '__main__':
# 創建兩個線程,分別執行兩個方法
run = threading.Thread(target=run_pytest)
gen = threading.Thread(target=general_report)
run.start()
# 先執行第一個線程,這個線程執行完才會執行下面的線程和主線程
run.join()
gen.start()
gen.join()
3.10 自動化執行生成結果
在windows下,右鍵執行main下面的run_test_allure_html.py(就是上一個步驟的python文件),然后打開report/allure_report/index.html看看報告是否生成成功
4. jenkins環境搭建(linux環境)
好了,到這一步,在windows下我們已經執行成功,現在我們要集成到jenkins環境去,並搭建在linux環境下。
1、將代碼上傳到公司的git(沒有git的自己搭建一套吧)
2、找一台linux機器(自己去自己公司找資源)
3、在linux下安裝jenkins(我是防止到tomcat中)、python3、pytest、allure、openpyxl(這些步驟在網上可以搜索到,這里不贅述了)
4、啟動linux下的tomcat,然后在window下打開jenkins的服務地址
5、創建一個自由風格的job
6、Job需要填寫的具體內容有:
- A、選擇丟棄舊的構建(保留的構建天數依據自己的情況選擇)
- B、“限制項目的運行節點”依據自己的情況選擇(我這里給我的jenkins主服務器取了一個叫linux的標簽,我的機器也是linux機器)
- C、git--將git上的代碼拉下來
- D、“執行shell”,這里把代碼從git拉到了jenkins的執行目錄里,一般在linux下的root/.jenkins里,在執行shell時,最好chmod修改下整個工程的目錄權限,因為有可能因權限問題執行不了
- E、構建后的操作:這里需要再jenkins里安裝allure插件才能看到allure Report,第一個Path,這里寫的是allure生成的json文件的目錄,所以是report/allure_result,第二個Report path指的是生成的index.html文件的目錄,所以是report/allure_report
4.2 立即構建並查看報告
上面的job建成后,就可以點擊立即構建,執行了。執行完后,點擊allure Report查看最終的報告。
5. 小結
今天的分享其實也只是全棧測開訓練營代碼級測試框架這個技能版塊中,很小的一部分知識點,里面涉及到的知識,在工作中都非常實用,非常值得學習。
此次分享就到這里了,算是一個階段學習總結吧。。雖然我是一只步入中年的測試老兵,但仍阻擋不了我持續學習前進的步伐,希望和那些有着同樣學習熱情的同學共勉。