很多人希望開源源碼, github上源碼:https://github.com/F1And/api4excel
喜歡給個(gè)star。
工作原理: 測(cè)試用例在excel上編輯,使用第三方庫xlrd,讀取表格sheet和內(nèi)容,sheetName對(duì)應(yīng)模塊名,Jenkins集成服務(wù)發(fā)現(xiàn)服務(wù)moduleName查找對(duì)應(yīng)表單,運(yùn)用第三方庫requests請(qǐng)求接口,根據(jù)結(jié)果和期望值進(jìn)行斷言,根據(jù)輸出報(bào)告判斷接口測(cè)試是否通過。
1. 數(shù)據(jù)準(zhǔn)備
- 數(shù)據(jù)插入(容易實(shí)現(xiàn)的測(cè)試場(chǎng)景下所需外部數(shù)據(jù))
- 準(zhǔn)備sql (接口需要重復(fù)使用,參數(shù)一定得是變量)
2.集成部署(運(yùn)維相關(guān)了解即可)
- 平滑升級(jí)驗(yàn)證腳本加入自動(dòng)化
3.自動(dòng)化框架實(shí)現(xiàn)
- 調(diào)用mysql
- excel遍歷測(cè)試用例
- requests實(shí)現(xiàn)接口調(diào)用
- 根據(jù)接口返回的code值和Excel對(duì)比
- 報(bào)告反饋
- 暴露服務(wù)
寫一個(gè)簡(jiǎn)單登錄的接口自動(dòng)化測(cè)試
代碼的分層如下圖:

代碼結(jié)構(gòu)
一、寫一個(gè)封裝的獲取excel表格的模塊

excel.png
代碼實(shí)現(xiàn)如下:
# !/usr/bin/python
# -*- coding: UTF-8 -*-
# author: 赫本z
# 基礎(chǔ)包: excel的封裝
import xlrd
workbook = None
def open_excel(path):
"""
打開excel
:param path: 打開excel文件的位置
"""
global workbook
if (workbook == None):
workbook = xlrd.open_workbook(path, on_demand=True)
def get_sheet(sheetName):
"""
獲取頁名
:param sheetName: 頁名
:return: workbook
"""
global workbook
return workbook.sheet_by_name(sheetName)
def get_rows(sheet):
"""
獲取行號(hào)
:param sheet: sheet
:return: 行數(shù)
"""
return sheet.nrows
def get_content(sheet, row, col):
"""
獲取表格中內(nèi)容
:param sheet: sheet
:param row: 行
:param col: 列
:return:
"""
return sheet.cell(row, col).value
def release(path):
"""釋放excel減少內(nèi)存"""
global workbook
workbook.release_resources()
del workbook
# todo:沒有驗(yàn)證是否可用
代碼封裝后當(dāng)成模塊引用,這還是最開始呢。
二、引用log模塊獲取日志
準(zhǔn)備工作:
需要一個(gè)日志的捕獲,包括框架和源碼拋出的expection。
代碼如下:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# author: 赫本z
# 基礎(chǔ)包: 日志服務(wù)
import logging
def get_logger():
global logPath
try:
logPath
except NameError:
logPath = ""
FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
logging.basicConfig(level=logging.INFO, format=FORMAT)
return logging
三、引用requests模塊接口測(cè)試
準(zhǔn)備工作:
需要的請(qǐng)求類型和執(zhí)行測(cè)試的方法。絕大多數(shù)公司使用Post和Get請(qǐng)求,一部分公司還在使用Restful API設(shè)計(jì)方案,可以因材施教。
代碼如下:
#!/usr/bin/python
#-*- coding: UTF-8 -*-
# 基礎(chǔ)包:接口測(cè)試的封裝
import requests
import core.log as log
import json
logging = log.get_logger()
def change_type(value):
"""
對(duì)dict類型進(jìn)行中文識(shí)別
:param value: 傳的數(shù)據(jù)值
:return: 轉(zhuǎn)碼后的值
"""
try:
if isinstance(eval(value), str):
return value
if isinstance(eval(value), dict):
result = eval(json.dumps(value))
return result
except Exception, e:
logging.error("類型問題 %s", e)
def api(method, url, data ,headers):
"""
自定義一個(gè)接口測(cè)試的方法
:param method: 請(qǐng)求類型
:param url: 地址
:param data: 數(shù)據(jù)
:param headers: 請(qǐng)求頭
:return: code碼
"""
global results
try:
if method == ("post" or "POST"):
results = requests.post(url, data, headers=headers)
if method == ("get" or "GET"):
results = requests.get(url, data, headers=headers)
# if method == "put":
# results = requests.put(url, data, headers=headers)
# if method == "delete":
# results = requests.delete(url, headers=headers)
# if method == "patch":
# results == requests.patch(url, data, headers=headers)
# if method == "options":
# results == requests.options(url, headers=headers)
response = results.json()
code = response.get("code")
return code
except Exception, e:
logging.error("service is error", e)
def content(method, url, data, headers):
"""
請(qǐng)求response自己可以自定義檢查結(jié)果
:param method: 請(qǐng)求類型
:param url: 請(qǐng)求地址
:param data: 請(qǐng)求參數(shù)
:param headers: 請(qǐng)求headers
:return: message信息
"""
global results
try:
if method == ("post" or "POST"):
results = requests.post(url, data, headers=headers)
if method == ("get" or "GET"):
results = requests.get(url, data, headers=headers)
if method == ("put" or "PUT"):
results = requests.put(url, data, headers=headers)
if method == ("patch" or "PATCH"):
results = requests.patch(url, data, headers=headers)
response = results.json()
message = response.get("message")
result = response.get("result")
content = {"message": message, "result": result}
return content
except Exception, e:
logging.error("請(qǐng)求失敗 %s" % e)
四、關(guān)于function模塊
主要調(diào)用二次封裝的代碼,結(jié)合業(yè)務(wù)做一個(gè)通用代碼。如下:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 業(yè)務(wù)包:通用函數(shù)
import core.mysql as mysql
import core.log as log
import core.request as request
import core.excel as excel
import constants as cs
from prettytable import PrettyTable
logging = log.get_logger()
class ApiTest:
"""接口測(cè)試業(yè)務(wù)類"""
filename = cs.FILE_NAME
def __init__(self):
pass
def prepare_data(self, host, user, password, db, sql):
"""數(shù)據(jù)準(zhǔn)備,添加測(cè)試數(shù)據(jù)"""
mysql.connect(host, user, password, db)
res = mysql.execute(sql)
mysql.close()
logging.info("Run sql: the row number affected is %s", res)
return res
def get_excel_sheet(self, path, module):
"""依據(jù)模塊名獲取sheet"""
excel.open_excel(path)
return excel.get_sheet(module)
def get_prepare_sql(self, sheet):
"""獲取預(yù)執(zhí)行SQL"""
return excel.get_content(sheet, cs.SQL_ROW, cs.SQL_COL)
def run_test(self, sheet, url):
"""再執(zhí)行測(cè)試用例"""
rows = excel.get_rows(sheet)
fail = 0
for i in range(2, rows):
testNumber = str(int(excel.get_content(sheet, i, cs.CASE_NUMBER)))
testData = excel.get_content(sheet, i, cs.CASE_DATA)
testName = excel.get_content(sheet, i, cs.CASE_NAME)
testUrl = excel.get_content(sheet, i, cs.CASE_URL)
testUrl = url + testUrl
testMethod = excel.get_content(sheet, i, cs.CASE_METHOD)
testHeaders = eval(excel.get_content(sheet, i, cs.CASE_HEADERS))
testCode = excel.get_content(sheet, i, cs.CASE_CODE)
actualCode = request.api(testMethod, testUrl, testData, testHeaders)
expectCode = str(int(testCode))
failResults = PrettyTable(["Number", "Method", "Url", "Data", "ActualCode", "ExpectCode"])
failResults.align["Number"] = "l"
failResults.padding_width = 1
failResults.add_row([testNumber, testMethod, testUrl, testData, actualCode, expectCode])
if actualCode != expectCode:
logging.info("FailCase %s", testName)
print "FailureInfo"
print failResults
fail += 1
else:
logging.info("Number %s", testNumber)
logging.info("TrueCase %s", testName)
if fail > 0:
return False
return True
五、關(guān)于參數(shù)中constans模塊
準(zhǔn)備工作:
所有的參數(shù)和常量我們會(huì)整理到這個(gè)文件中,因?yàn)樵O(shè)計(jì)業(yè)務(wù)和服務(wù)密碼、數(shù)據(jù)庫密碼這里展示一部分。
代碼如下:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 通用包:常量
CASE_NUMBER = 0 # 用例編號(hào)
CASE_NAME = 1 # 用例名稱
CASE_DATA = 2 # 用例參數(shù)
CASE_URL = 3 # 用例接口地址
CASE_METHOD = 4 # 用例請(qǐng)求類型
CASE_CODE = 5 # 用例code
CASE_HEADERS = 6 # 用例headers
SQL_ROW = 0 # 預(yù)執(zhí)行SQL的行號(hào)
SQL_COL = 1 # 預(yù)執(zhí)行SQL的列號(hào)
FILE_NAME = 'test.xlsx'
六、寫一個(gè)run文件:只是用來執(zhí)行的,業(yè)務(wù)和代碼剝離。
代碼如下:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 驗(yàn)證包:接口測(cè)試腳本
import core.log as log
from function.func import ApiTest
func = ApiTest()
logging = log.get_logger()
"""1.外部輸入?yún)?shù)"""
module = 'user'
url = 'http://127.0.0.1:8080'
"""2.根據(jù)module獲取Sheet"""
logging.info("-------------- Execute TestCases ---------------")
sheet = func.get_excel_sheet(func.filename, module)
# """3.數(shù)據(jù)準(zhǔn)備"""
# logging.info("-------------- Prepare data through MysqlDB --------------")
# sql = func.get_prepare_sql(sheet)
# func.prepare_data(host=host, user=user, password=password, db=db, sql=sql)
"""4.執(zhí)行測(cè)試用例"""
res = func.run_test(sheet, url)
logging.info("-------------- Get the result ------------ %s", res)
七、查看測(cè)試報(bào)告(部署到j(luò)enkins會(huì)通過控制臺(tái)查看)

報(bào)告.png