百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

「手把手教python3接口自动化」: 搭建接口自动化测试框架

off999 2024-10-17 11:46 91 浏览 0 评论

「第二十六章」 搭建接口自动化测试框架

26.1 为什么要开发Python3 接口测试框架

先看教育局招生管理系统登录接口用例。

import requests

from bs4 import BeautifulSoup

import xlrd

import json

def readExcel(rowx, filePath='data.xls'):

'''

读取excel中数据并且返回

:parameter filePath:xlsx文件名称

:parameter rowx:在excel中的行数

'''

book = xlrd.open_workbook(filePath)

sheet = book.sheet_by_index(0)

return sheet.row_values(rowx)

#提取第一个测试用例数据

print("第一行数据内容:",readExcel(2))

#查看数据的类型

print("数据类型:{0}:".format(type(readExcel(2))))

# 从列表中提取url

url = readExcel(2)[3]

print(url)

# 从列表中提取data参数

# 由于JSON中,标准语法中,不支持单引号,属性或者属性值,都必须是双引号括起来,用字符串方法replace("'",'\"')进行处理

data1 =readExcel(2)[4].replace("'",'\"')

#因为请求参数数据类型是字典,所以进行了反序列化的处理。

data = json.loads(data1)

print(data)

print(type(data))

print("---------------------------------------------")

#招生系统接口例子

headers = {

"Connection": "keep-alive",

"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",

"Referer": "http://127.0.0.1:8090/recruit.students/login/view",

}

# URL参数

payload = data

# 发送get请求

response = requests.get(url,headers = headers,params=payload)

# 打印请求response后的URL

print(response.url)

# 查看响应内容,response.text 返回的是Unicode格式的数据

print(response.text)

# 查看响应码

print(response.status_code)

从教育局招生管理系统登录接口用例中,我们看到,要完成一个接口测试,需要写很多行的代码,一个完整的系统测试,测试用例可能有100~1000个,那么如果每个用例都一个一个这样写,那么编写用例和维护用例的成本就很高,而且代码很多,不直观。

随着自动化测试的不断推广,各家软件科技公司也不断的引进了专门的自动化测试人员,一个完整的系统测试,测试用例中有许多共用的模块和方法,不需要每个人都重复去写,这就需要有资深的测试专家去规划一个项目的自动化测试框架。

同时,接口测试生成测试报告,发送给相关的领导,也是需要一个完整的接口自动化测试框架。

26.2 Python3 接口自动化测试框架介绍

a) 基于 python 脚本编写,不需要借助其他工具,环境配置简单。

b) 采用数据驱动测试的方式,后期仅仅只需要维护一个测试数据。

c) 测试用例可以分模块编写,方便对用例的管理维护。

d) 测试用例和测试执行分离,实现执行测试时用例可以任意配置。

e) 实现多线程执行,可以同时运行多个测试集。

f) 测试结束后直接生成测试报告,统计测试执行情况,执行情况详细,能够快速定位问题,并且容易扩展优化。

26.3 框架流程说明

26.4 框架的目录结构

【common】:存放公共的方法。

【result】:执行过程中生成的文件夹,里面存放每次测试的结果。

【testCase】:用于存放具体的测试case。

【testFile】:存放测试过程中用到的文件,包括上传的文件,测试用例数据驱动以及数据库的sql语句。

【caselist】:caselist.txt文件,配置每次执行的case名称。

【config】:配置一些常量,例如数据库的相关信息,接口的相关信息等。

【readConfig】:用于读取config配置文件中的内容

【runAll】:用于执行case,生成测试报告。

上面是整个Python3接口测试框架的目录结构,下面对每一块进行介绍。

26.5 config.ini 和 readConfig.py

ini 文件其实就是所谓的初始化配置文件,一般的格式为:

[SECTION0]

key0 = value0

key1 = value1

config.ini 文件的内容:

[EMAIL]

mail_host = smtp.163.com

mail_user = abc@163.com

mail_pass = 123456

mail_port = 25

sender = abc@163.com

receiver = 123456@qq.com/1254367@qq.com

subject = Interface Test Report

content = "All interface test has been complited\nplease read the report file about the detile of result in the attachment."

testuser = Someone

on_off = off

[HTTP]

scheme = http

baseurl = www.baidu.com

port = 8080

timeout = 10.0

[HEADERS]

siteuid = all

clientid = 100

token_v = 213612368807_5811a363c85b19.22399915_75e4ee2761d60f2d7597eaec2579297f1cd7f6e3

token_u = 320012369021_586b1c65c3b3c1.51719831_e76832dc51f7ec8de0ba6ecdd69c8b7658dee93c

[DATABASE]

host = localhost

username = root

password = root

port = 3306

database = test

这个文件是整个Python3接口测试框架的配置文件,主要用来配置一些固定不变的参数。我们在测试用例中使用这些配置文件的内容,就需要使用readConfig.py去读取。

readConfig.py文件的代码。

import os

#导入codecs模块,使用codecs模块进行文件操作

import codecs

#导入configparser模块,python3 用ConfigParser包处理 ini文件

import configparser

#获取config.ini 文件路径

proDir = os.path.split(os.path.realpath(__file__))[0]

configPath = os.path.join(proDir, "config.ini")

class ReadConfig:

def __init__(self):

fd = open(configPath)

data = fd.read()

# remove BOM

if data[:3] == codecs.BOM_UTF8:

data = data[3:]

file = codecs.open(configPath, "w")

file.write(data)

file.close()

fd.close()

self.cf = configparser.ConfigParser()

self.cf.read(configPath)

def get_email(self, name):

value = self.cf.get("EMAIL", name)

return value

def get_http(self, name):

value = self.cf.get("HTTP", name)

return value

def get_headers(self, name):

value = self.cf.get("HEADERS", name)

return value

def set_headers(self, name, value):

self.cf.set("HEADERS", name, value)

with open(configPath, 'w+') as f:

self.cf.write(f)

def get_url(self, name):

value = self.cf.get("URL", name)

return value

def get_db(self, name):

value = self.cf.get("DATABASE", name)

return value

readConfig.py文件定义了相应的方法,根据名称取对应的值。

26.6 common 文件夹

配置文件和读取配置文件已经介绍,接下来就可以写common里的共通方法。

【Log.py】:Log 模块是一个独立的模块,对输出的日志的所有操作。

【configHttp.py】:configHttp 模块是接口配置文件。

【common】:common 模块 是数据驱动的配置文件。

【configDB 模块】:configDB 模块主要是封装了操作mysql数据的相关操作方法。

【configEmail 模块】:configEmail 模块主要是封装了发送邮件的相关方法。

【businessCommon 模块】:businessCommon 模块封装了登录和登录退出的方法。

26.6.1 Log.py 模块

Log.py 模块主要是对输出格式的规定,输出等级的定义以及其他一些输出的定义等。

import os,sys

import readConfig as readConfig

import logging

from datetime import datetime

import threading

localReadConfig = readConfig.ReadConfig()

class Log:

def __init__(self):

global logPath, resultPath, proDir

proDir = readConfig.proDir

resultPath = os.path.join(proDir, "result")

if not os.path.exists(resultPath):

os.mkdir(resultPath)

logPath = os.path.join(resultPath, str(datetime.now().strftime("%Y%m%d%H%M%S")))

if not os.path.exists(logPath):

os.mkdir(logPath)

self.logger = logging.getLogger()

self.logger.setLevel(logging.INFO)

# defined handler

handler = logging.FileHandler(os.path.join(logPath, "output.log"))

# defined formatter

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

handler.setFormatter(formatter)

self.logger.addHandler(handler)

def get_logger(self):

"""

get logger

:return:

"""

return self.logger

def build_start_line(self, case_no):

"""

write start line

:return:

"""

self.logger.info("--------" + case_no + " START--------")

def build_end_line(self, case_no):

"""

write end line

:return:

"""

self.logger.info("--------" + case_no + " END--------")

def build_case_line(self, case_name, code, msg):

"""

write test case line

:param case_name:

:param code:

:param msg:

:return:

"""

self.logger.info(case_name+" - Code:"+code+" - msg:"+msg)

def get_report_path(self):

"""

get report file path

:return:

"""

report_path = os.path.join(logPath, "report.html")

return report_path

def get_result_path(self):

"""

get test result path

:return:

"""

return logPath

def write_result(self, result):

"""

:param result:

:return:

"""

result_path = os.path.join(logPath, "report.txt")

fb = open(result_path, "wb")

try:

fb.write(result)

except FileNotFoundError as ex:

logger.error(str(ex))

class MyLog:

log = None

mutex = threading.Lock()

def __init__(self):

pass

@staticmethod

def get_log():

if MyLog.log is None:

MyLog.mutex.acquire()

MyLog.log = Log()

MyLog.mutex.release()

return MyLog.log

if __name__ == "__main__":

log = MyLog.get_log()

logger = log.get_logger()

logger.debug("test debug")

logger.info("test info")

26.6.2 configHttp.py 模块

configHttp.py 模块代码:

import requests

import readConfig as readConfig

from common.Log import MyLog as Log

import json

localReadConfig = readConfig.ReadConfig()

class ConfigHttp:

def __init__(self):

global scheme, host, port, timeout

scheme = localReadConfig.get_http("scheme")

host = localReadConfig.get_http("baseurl")

port = localReadConfig.get_http("port")

timeout = localReadConfig.get_http("timeout")

self.log = Log.get_log()

self.logger = self.log.get_logger()

self.headers = {}

self.params = {}

self.data = {}

self.url = None

self.files = {}

self.state = 0

def set_url(self, url):

"""

set url

:param: interface url

:return:

"""

self.url = scheme+'://'+host+url

def set_headers(self, header):

"""

set headers

:param header:

:return:

"""

self.headers = header

def set_params(self, param):

"""

set params

:param param:

:return:

"""

self.params = param

def set_data(self, data):

"""

set data

:param data:

:return:

"""

self.data = data

def set_files(self, filename):

"""

set upload files

:param filename:

:return:

"""

if filename != '':

file_path = 'F:/AppTest/Test/interfaceTest/testFile/img/' + filename

self.files = {'file': open(file_path, 'rb')}

if filename == '' or filename is None:

self.state = 1

# defined http get method

def get(self):

"""

defined get method

:return:

"""

try:

response = requests.get(self.url, headers=self.headers, params=self.params, timeout=float(timeout))

# response.raise_for_status()

return response

except TimeoutError:

self.logger.error("Time out!")

return None

# defined http post method

# include get params and post data

# uninclude upload file

def post(self):

"""

defined post method

:return:

"""

try:

response = requests.post(self.url, headers=self.headers, params=self.params, data=self.data, timeout=float(timeout))

# response.raise_for_status()

return response

except TimeoutError:

self.logger.error("Time out!")

return None

# defined http post method

# include upload file

def postWithFile(self):

"""

defined post method

:return:

"""

try:

response = requests.post(self.url, headers=self.headers, data=self.data, files=self.files, timeout=float(timeout))

return response

except TimeoutError:

self.logger.error("Time out!")

return None

# defined http post method

# for json

def postWithJson(self):

"""

defined post method

:return:

"""

try:

response = requests.post(self.url, headers=self.headers, json=self.data, timeout=float(timeout))

return response

except TimeoutError:

self.logger.error("Time out!")

return None

if __name__ == "__main__":

print("ConfigHTTP")

configHttp.py 模块说明:

接口测试框架用python3自带的requests来进行接口请求测试。里面封装了get和post两个方法。

【get方法】

对于requests提供的get方法,有几个常用的参数:

url:接口的url地址。

headers:定制请求头(headers),例如:content-type = application/x-www-form-urlencoded。

params:用于传递测试接口所要用的参数,这里我们用python中的字典形式(key:value)进行参数的传递。

timeout:设置接口连接的最大时间(超过该时间会抛出超时错误)

例子:

url=‘http://api.shein.com/v2/member/logout’

header={‘content-type’: application/x-www-form-urlencoded}

param={‘user_id’: 123456,‘email’: 123456@163.com}

timeout=0.5

requests.get(url, headers=header, params=param, timeout=timeout)

【post方法】

与get方法类似,只要设置好对应的参数,就可以了

例子:

url=‘http://api.shein.com/v2/member/login’

header={‘content-type’: application/x-www-form-urlencoded}

data={‘email’: 123456@163.com,‘password’: 123456}

timeout=0.5

requests.post(url, headers=header, data=data, timeout=timeout)

备注:post 方法中的参数,我们不是使用 params 进行传递,而是改用 data 进行传递。

26.6.3 common.py 模块

common.py 模块代码:

import requests

import readConfig as readConfig

import os

from xlrd import open_workbook

from xml.etree import ElementTree as ElementTree

from common import configHttp as configHttp

from common.Log import MyLog as Log

import json

localReadConfig = readConfig.ReadConfig()

proDir = readConfig.proDir

localConfigHttp = configHttp.ConfigHttp()

log = Log.get_log()

logger = log.get_logger()

caseNo = 0

def get_visitor_token():

"""

create a token for visitor

:return:

"""

host = localReadConfig.get_http("BASEURL")

response = requests.get(host+"/v2/User/Token/generate")

info = response.json()

token = info.get("info")

logger.debug("Create token:%s" % (token))

return token

def set_visitor_token_to_config():

"""

set token that created for visitor to config

:return:

"""

token_v = get_visitor_token()

localReadConfig.set_headers("TOKEN_V", token_v)

def get_value_from_return_json(json, name1, name2):

"""

get value by key

:param json:

:param name1:

:param name2:

:return:

"""

info = json['info']

group = info[name1]

value = group[name2]

return value

def show_return_msg(response):

"""

show msg detail

:param response:

:return:

"""

url = response.url

msg = response.text

print("\n请求地址:"+url)

# 可以显示中文

print("\n请求返回值:"+'\n'+json.dumps(json.loads(msg), ensure_ascii=False, sort_keys=True, indent=4))

# ****************************** read testCase excel ********************************

def get_xls(xls_name, sheet_name):

"""

get interface data from xls file

:return:

"""

cls = []

# get xls file's path

xlsPath = os.path.join(proDir, "testFile", 'case', xls_name)

# open xls file

file = open_workbook(xlsPath)

# get sheet by name

sheet = file.sheet_by_name(sheet_name)

# get one sheet's rows

nrows = sheet.nrows

for i in range(nrows):

if sheet.row_values(i)[0] != u'case_name':

cls.append(sheet.row_values(i))

return cls

# ****************************** read SQL xml ********************************

database = {}

def set_xml():

"""

set sql xml

:return:

"""

if len(database) == 0:

sql_path = os.path.join(proDir, "testFile", "SQL.xml")

tree = ElementTree.parse(sql_path)

for db in tree.findall("database"):

db_name = db.get("name")

# print(db_name)

table = {}

for tb in db.getchildren():

table_name = tb.get("name")

# print(table_name)

sql = {}

for data in tb.getchildren():

sql_id = data.get("id")

# print(sql_id)

sql[sql_id] = data.text

table[table_name] = sql

database[db_name] = table

def get_xml_dict(database_name, table_name):

"""

get db dict by given name

:param database_name:

:param table_name:

:return:

"""

set_xml()

database_dict = database.get(database_name).get(table_name)

return database_dict

def get_sql(database_name, table_name, sql_id):

"""

get sql by given name and sql_id

:param database_name:

:param table_name:

:param sql_id:

:return:

"""

db = get_xml_dict(database_name, table_name)

sql = db.get(sql_id)

return sql

# ****************************** read interfaceURL xml ********************************

def get_url_from_xml(name):

"""

By name get url from interfaceURL.xml

:param name: interface's url name

:return: url

"""

url_list = []

url_path = os.path.join(proDir, 'testFile', 'interfaceURL.xml')

tree = ElementTree.parse(url_path)

for u in tree.findall('url'):

url_name = u.get('name')

if url_name == name:

for c in u.getchildren():

url_list.append(c.text)

url = '/v2/' + '/'.join(url_list)

return url

if __name__ == "__main__":

print(get_xls("login"))

set_visitor_token_to_config()

common.py 模块主要是封装了数据驱动的各种方法(读取xml文件、读取excel文件,读取mysql数据库数据),测试用例就是通过 excel 文件来管理测试用例的。

26.6.4 configDB 模块

configDB 模块代码:

import pymysql

import readConfig as readConfig

from common.Log import MyLog as Log

localReadConfig = readConfig.ReadConfig()

class MyDB:

global host, username, password, port, database, config

host = localReadConfig.get_db("host")

username = localReadConfig.get_db("username")

password = localReadConfig.get_db("password")

port = localReadConfig.get_db("port")

database = localReadConfig.get_db("database")

config = {

'host': str(host),

'user': username,

'passwd': password,

'port': int(port),

'db': database

}

def __init__(self):

self.log = Log.get_log()

self.logger = self.log.get_logger()

self.db = None

self.cursor = None

def connectDB(self):

"""

connect to database

:return:

"""

try:

# connect to DB

self.db = pymysql.connect(**config)

# create cursor

self.cursor = self.db.cursor()

print("Connect DB successfully!")

except ConnectionError as ex:

self.logger.error(str(ex))

def executeSQL(self, sql, params):

"""

execute sql

:param sql:

:return:

"""

self.connectDB()

# executing sql

self.cursor.execute(sql, params)

# executing by committing to DB

self.db.commit()

return self.cursor

def get_all(self, cursor):

"""

get all result after execute sql

:param cursor:

:return:

"""

value = cursor.fetchall()

return value

def get_one(self, cursor):

"""

get one result after execute sql

:param cursor:

:return:

"""

value = cursor.fetchone()

return value

def closeDB(self):

"""

close database

:return:

"""

self.db.close()

print("Database closed!")

configDB 模块主要是封装了操作mysql数据的相关操作方法(连接数据库,执行sql,获取结果,最后关闭数据库)。

26.6.5 configEmail 模块

configEmail 模块代码:

import os

import smtplib

from email.mime.multipart import MIMEMultipart

from email.mime.text import MIMEText

from email.mime.image import MIMEImage

from datetime import datetime

import threading

import readConfig as readConfig

from common.Log import MyLog

import zipfile

import glob

localReadConfig = readConfig.ReadConfig()

class Email:

def __init__(self):

global host, user, password, port, sender, title

host = localReadConfig.get_email("mail_host")

user = localReadConfig.get_email("mail_user")

password = localReadConfig.get_email("mail_pass")

port = localReadConfig.get_email("mail_port")

sender = localReadConfig.get_email("sender")

title = localReadConfig.get_email("subject")

# content = localReadConfig.get_email("content")

# get receiver list

self.value = localReadConfig.get_email("receiver")

self.receiver = []

for n in str(self.value).split("/"):

self.receiver.append(n)

# defined email subject

date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

self.subject = "接口测试报告" + " " + date

self.log = MyLog.get_log()

self.logger = self.log.get_logger()

self.msg = MIMEMultipart('related')

def config_header(self):

"""

defined email header include subject, sender and receiver

:return:

"""

self.msg['subject'] = self.subject

self.msg['from'] = sender

self.msg['to'] = ";".join(self.receiver)

def config_content(self):

"""

write the content of email

:return:

"""

f = open(os.path.join(readConfig.proDir, 'testFile', 'emailStyle.txt'))

content = f.read()

f.close()

content_plain = MIMEText(content, 'html', 'UTF-8')

self.msg.attach(content_plain)

self.config_image()

def config_image(self):

"""

config image that be used by content

:return:

"""

# defined image path

image1_path = os.path.join(readConfig.proDir, 'testFile', 'img', '1.png')

fp1 = open(image1_path, 'rb')

msgImage1 = MIMEImage(fp1.read())

# self.msg.attach(msgImage1)

fp1.close()

# defined image id

msgImage1.add_header('Content-ID', '<image1>')

self.msg.attach(msgImage1)

image2_path = os.path.join(readConfig.proDir, 'testFile', 'img', 'logo.jpg')

fp2 = open(image2_path, 'rb')

msgImage2 = MIMEImage(fp2.read())

# self.msg.attach(msgImage2)

fp2.close()

# defined image id

msgImage2.add_header('Content-ID', '<image2>')

self.msg.attach(msgImage2)

def config_file(self):

"""

config email file

:return:

"""

# if the file content is not null, then config the email file

if self.check_file():

reportpath = self.log.get_result_path()

zippath = os.path.join(readConfig.proDir, "result", "test.zip")

# zip file

files = glob.glob(reportpath + '\*')

f = zipfile.ZipFile(zippath, 'w', zipfile.ZIP_DEFLATED)

for file in files:

# 修改压缩文件的目录结构

f.write(file, '/report/'+os.path.basename(file))

f.close()

reportfile = open(zippath, 'rb').read()

filehtml = MIMEText(reportfile, 'base64', 'utf-8')

filehtml['Content-Type'] = 'application/octet-stream'

filehtml['Content-Disposition'] = 'attachment; filename="test.zip"'

self.msg.attach(filehtml)

def check_file(self):

"""

check test report

:return:

"""

reportpath = self.log.get_report_path()

if os.path.isfile(reportpath) and not os.stat(reportpath) == 0:

return True

else:

return False

def send_email(self):

"""

send email

:return:

"""

self.config_header()

self.config_content()

self.config_file()

try:

smtp = smtplib.SMTP()

smtp.connect(host)

smtp.login(user, password)

smtp.sendmail(sender, self.receiver, self.msg.as_string())

smtp.quit()

self.logger.info("The test report has send to developer by email.")

except Exception as ex:

self.logger.error(str(ex))

class MyEmail:

email = None

mutex = threading.Lock()

def __init__(self):

pass

@staticmethod

def get_email():

if MyEmail.email is None:

MyEmail.mutex.acquire()

MyEmail.email = Email()

MyEmail.mutex.release()

return MyEmail.email

if __name__ == "__main__":

email = MyEmail.get_email()

configEmail 模块主要是封装了发送邮件的相关方法(每次测试完之后,都会生成一份测试报告,并且把测试报告以附件的形式,通过email发送给相关的邮箱)。

26.6.6 businessCommon 模块

businessCommon 模块代码:

from common import common

from common import configHttp

import readConfig as readConfig

localReadConfig = readConfig.ReadConfig()

localConfigHttp = configHttp.ConfigHttp()

localLogin_xls = common.get_xls("userCase.xlsx", "login")

localAddAddress_xls = common.get_xls("userCase.xlsx", "addAddress")

# login

def login():

"""

login

:return: token

"""

# set url

url = common.get_url_from_xml('login')

localConfigHttp.set_url(url)

# set header

token = localReadConfig.get_headers("token_v")

header = {"token": token}

localConfigHttp.set_headers(header)

# set param

data = {"email": localLogin_xls[0][3],

"password": localLogin_xls[0][4]}

localConfigHttp.set_data(data)

# login

response = localConfigHttp.post().json()

token = common.get_value_from_return_json(response, "member", "token")

return token

# logout

def logout(token):

"""

logout

:param token: login token

:return:

"""

# set url

url = common.get_url_from_xml('logout')

localConfigHttp.set_url(url)

# set header

header = {'token': token}

localConfigHttp.set_headers(header)

# logout

localConfigHttp.get()

businessCommon 模块封装了登录和登录退出的方法,因为每个测试用例都用到。

26.7 runAll 文件

runAll 文件代码:

import os

import unittest

import time as t

from common.Log import MyLog as Log

import readConfig as readConfig

import HTMLTestRunner

from common.configEmail import MyEmail

localReadConfig = readConfig.ReadConfig()

class AllTest:

def __init__(self):

global log, logger, resultPath, on_off

log = Log.get_log()

logger = log.get_logger()

resultPath = log.get_report_path()

on_off = localReadConfig.get_email("on_off")

self.caseListFile = os.path.join(readConfig.proDir, "caselist.txt")

self.caseFile = os.path.join(readConfig.proDir, "testCase")

# self.caseFile = None

self.caseList = []

self.email = MyEmail.get_email()

def set_case_list(self):

"""

set case list

:return:

"""

fb = open(self.caseListFile)

for value in fb.readlines():

data = str(value)

if data != '' and not data.startswith("#"):

self.caseList.append(data.replace("\n", ""))

fb.close()

def set_case_suite(self):

"""

set case suite

:return:

"""

self.set_case_list()

test_suite = unittest.TestSuite()

suite_module = []

for case in self.caseList:

case_name = case.split("/")[-1]

print(case_name+".py")

discover = unittest.defaultTestLoader.discover(self.caseFile, pattern=case_name + '.py', top_level_dir=None)

suite_module.append(discover)

if len(suite_module) > 0:

for suite in suite_module:

for test_name in suite:

test_suite.addTest(test_name)

else:

return None

return test_suite

def run(self):

"""

run test

:return:

"""

try:

suit = self.set_case_suite()

if suit is not None:

logger.info("********TEST START********")

fp = open(resultPath, 'wb')

runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title='Test Report', description='Test Description')

runner.run(suit)

else:

logger.info("Have no case to test.")

except Exception as ex:

logger.error(str(ex))

finally:

logger.info("*********TEST END*********")

fp.close()

# send test report by email

if on_off == 'on':

self.email.send_email()

elif on_off == 'off':

logger.info("Doesn't send report email to developer.")

else:

logger.info("Unknow state.")

if __name__ == '__main__':

obj = AllTest()

obj.run()

runAll的执行原理,首先我们要从 caselist.txt 文件中读取需要执行的 case 名称,然后将他们添加到 python3 自带的 unittest 测试集的测试套件中,最后执行run()函数,执行测试集。

26.8 result 文件

result文件夹会在首次执行case时生成,并且以后的测试结果都会被保存在该文件夹下,同时每次测试的文件夹都是用系统时间命名,里面包含了两个文件,log文件和测试报告。

26.9 caselist.txt文件

caselist.txt文件存放了要执行的测试用例名称,凡是没有被注释掉的,都是要被执行的case名称。

26.10 testFile文件夹

testFile文件夹下,case文件夹下放置我们测试时用来管理测试用例, img下放用于数据库查询的sql语句的xml文件、邮件的样式等。

26.11 testCase文件夹

testCase文件夹下,存放我们写的具体的测试用例,测试用例目录可以根据模块来划分,也可以根据其他特定需求来划分。

注意:所有的 case 名称都要以 test 开头来命名,这是因为 Python3 unittest 在进行测试时会自动匹配 testCase 文件夹下面所有 test 开头的 .py 文件。

26.12 汇总

对 Python3 的接口测试框架简单介绍了一下,其实大家要学习的是封装的思想,如何把一些公共的模块抽象出来,达到数据与业务流程的分离,在上面的框架中,我把常用固定的参数放到 config.ini 文件中,测试用例的管理(excel文件),sql 语句的存放(xml文件),每个公司的项目不同,要求的不一样,自己可以根据自己项目的需求去封装自己的接口测试框架。

相关推荐

安全教育登录入口平台(安全教育登录入口平台官网)

122交通安全教育怎么登录:122交通网的注册方法是首先登录网址http://www.122.cn/,接着打开网页后,点击右上角的“个人登录”;其次进入邮箱注册,然后进入到注册页面,输入相关信息即可完...

大鱼吃小鱼经典版(大鱼吃小鱼经典版(经典版)官方版)

大鱼吃小鱼小鱼吃虾是于谦跟郭麒麟的《我的棒儿呢?》郭德纲说于思洋郭麒麟作诗的相声,最后郭麒麟做了一首,师傅躺在师母身上大鱼吃小鱼小鱼吃虾虾吃水水落石出师傅压师娘师娘压床床压地地动山摇。...

谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
哪个软件可以免费pdf转ppt(免费的pdf转ppt软件哪个好)
哪个软件可以免费pdf转ppt(免费的pdf转ppt软件哪个好)

要想将ppt免费转换为pdf的话,我们建议大家可以下一个那个wps,如果你是会员的话,可以注册为会员,这样的话,在wps里面的话,就可以免费将ppt呢转换为pdfpdf之后呢,我们就可以直接使用,不需要去直接不需要去另外保存,为什么格式转...

2026-02-04 09:03 off999

电信宽带测速官网入口(电信宽带测速官网入口app)

这个网站看看http://www.swok.cn/pcindex.jsp1.登录中国电信网上营业厅,宽带光纤,贴心服务,宽带测速2.下载第三方软件,如360等。进行在线测速进行宽带测速时,尽...

植物大战僵尸95版手机下载(植物大战僵尸95 版下载)

1可以在应用商店或者游戏平台上下载植物大战僵尸95版手机游戏。2下载教程:打开应用商店或者游戏平台,搜索“植物大战僵尸95版”,找到游戏后点击下载按钮,等待下载完成即可安装并开始游戏。3注意:确...

免费下载ppt成品的网站(ppt成品免费下载的网站有哪些)

1、Chuangkit(chuangkit.com)直达地址:chuangkit.com2、Woodo幻灯片(woodo.cn)直达链接:woodo.cn3、OfficePlus(officeplu...

2025世界杯赛程表(2025世界杯在哪个国家)

2022年卡塔尔世界杯赛程公布,全部比赛在卡塔尔境内8座球场举行,2022年,决赛阶段球队全部确定。揭幕战于当地时间11月20日19时进行,由东道主卡塔尔对阵厄瓜多尔,决赛于当地时间12月18日...

下载搜狐视频电视剧(搜狐电视剧下载安装)

搜狐视频APP下载好的视频想要导出到手机相册里方法如下1、打开手机搜狐视频软件,进入搜狐视频后我们点击右上角的“查找”,找到自已喜欢的视频。2、在“浏览器页面搜索”窗口中,输入要下载的视频的名称,然后...

pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
永久免费听歌网站(丫丫音乐网)

可以到《我爱音乐网》《好听音乐网》《一听音乐网》《YYMP3音乐网》还可以到《九天音乐网》永久免费听歌软件有酷狗音乐和天猫精灵,以前要跳舞经常要下载舞曲,我从QQ上找不到舞曲下载就从酷狗音乐上找,大多...

音乐格式转换mp3软件(音乐格式转换器免费版)

有两种方法:方法一在手机上操作:1、进入手机中的文件管理。2、在其中选择“音乐”,将显示出手机中的全部音乐。3、点击“全选”,选中所有音乐文件。4、点击屏幕右下方的省略号图标,在弹出菜单中选择“...

电子书txt下载(免费的最全的小说阅读器)

1.Z-library里面收录了近千万本电子书籍,需求量大。2.苦瓜书盘没有广告,不需要账号注册,使用起来非常简单,直接搜索预览下载即可。3.鸠摩搜书整体风格简洁清晰,书籍资源丰富。4.亚马逊图书书籍...

最好免费观看高清电影(播放免费的最好看的电影)

在目前的网上选择中,IMDb(互联网电影数据库)被认为是最全的电影网站之一。这个网站提供了各种类型的电影和电视节目的海量信息,包括剧情介绍、演员表、评价、评论等。其还提供了有关电影制作背后的详细信息,...

孤单枪手2简体中文版(孤单枪手2简体中文版官方下载)

要将《孤胆枪手2》游戏的征兵秘籍切换为中文,您可以按照以下步骤进行操作:首先,打开游戏设置选项,通常可以在游戏主菜单或游戏内部找到。然后,寻找语言选项或界面选项,点击进入。在语言选项中,选择中文作为游...

取消回复欢迎 发表评论: