基于txt搭建接口自动化框架

最后输出的结果效果:

本框架最后的文件结构如下图所示 

Util 文件:
在 Util.py 文件中,我们封装了程序主体功能,包括构建唯一电话号码、发送
请求获取返回结果和结果断言,函数具体功能如下:
1. get_uniquenumber:处理用户名里面的数字,使用户名唯一,该函数使用
uniquenumber.txt 文件
2. send_request:发送请求,获取返回结果,send_request 函数处理请求数据类型为
字典格式和非字典格式,请求类型为 post,get,put 等类型的请求
3. assert_result:断言结果

# encoding = utf-8
import requests


def get_uniquenumber():
    """
    @param data_file:
    @return:
    !!!前置条件:创建 uniquenumber.txt 文件存储数字,然后每次读取 uniquenumber 里面的数字,
使用后将数字+1 再写回去,可保证数字完全不会重复使用
    """
    with open("uniquenumber.txt", "r+", encoding="utf-8") as fp:
        uniquenumber = int(fp.read().strip())
        fp.seek(0, 0)
        fp.write(str(uniquenumber + 1))
        # print("uniquenumber:{}".format(uniquenumber))
        return uniquenumber


def send_request(url, data, headers, request_type):
    """
    请求接口,得到返回数据
    """
    # print("res in send_request:{}".format(res))
    if request_type == "post":
        res = requests.post(url, data=data, headers=headers)
    elif request_type == "get":
        res = requests.get(url, data=data, headers=headers)
    elif request_type == "put":
        res = requests.put(url, data=data, headers=headers)
    return res


def assert_result(response, key_word):
    """
    断言函数
    @param response: 结果响应对象
    @param key_word: 断言关键字
    @return:
    """
    try:
        assert key_word in response.text  # 断言注册成功
        return True
    except AssertionError as e:
        return False
    except:
        return False

uniquenumber.txt 文件:
该文件内容只有一个数字,用于在 get_uniquenumber()函数中,返回唯一的数字并更新这个
文件,返回的数字会拼接用户名字符串,生成唯一的用户名,避免在注册接口中出现重复的
情况。


server_info.py 文件:
在 server_info.py 文件中我们放入接口的 url 信息,其中 ip 和端口用变量的形式存储,各个
接口的地址是以字符串替换的方式保存在对应的变量中,方便统一为维护,内容如下所示

 test_data.py 文件
在 test_data.py 文件中我们放入测试用例数据,每一条数据表示一条用例(在文件中,一条
数据对应一行数据),用例中不同的数据信息用”||”来分隔。
注意,第一部分内容看上去是表示用例的名称(如”register”, “login”等),实际上这个关键
字对应的是在 server_info.py 中存储的变量,在经过处理后,这个关键字会被当做变量来处
理,并用 server_info.py 中同名变量存储的值所代替。
那么,test_data.py 中每一条数据从左到右依次表示为接口地址、接口请求数据(包含使用
参数化和使用关联)、断言关键字、关联表达式和请求方式共 5 个数据。
其中参数化部分是以”${}”为标识,使用关联以”%{}”为标识

register||{"mobile": "138${unique_num1}", "codeIdentify": 2, "platform": 2}||"code":0||sms_code----"code":"(\w+)"||post
login||{"phone": "138${num1}", "sms_code": "%{sms_code}", "client_id": "c4", "client_secret": "secret", "grant_type": "sms_code"}||"access_token":||access_token----"access_token":"(\S+?)"||post
register||{"mobile": "138${unique_num1}", "codeIdentify": 1, "platform": 2}||"code":0||sms_code----"code":"(\w+)"||post
login||{"phone": "138${num1}", "sms_code": "%{sms_code}", "client_id": "c4", "client_secret": "secret", "grant_type": "sms_code"}||"access_token":||access_token----"access_token":"(\S+?)"||post
register||{"mobile": "138${unique_num1}", "codeIdentify": 2, "platform":  }||"code":0||sms_code----"code":"(\w+)"||post
login||{"phone": "138${num1}", "sms_code": "%{sms_code}", "client_id": "c4", "client_secret": "secret", "grant_type": "sms_code"}||"access_token":||access_token----"access_token":"(\S+?)"||post
register||{"mobile": "138${unique_num1}", "codeIdentify": 0, "platform": 2}||"code":0||sms_code----"code":"(\w+)"||post
login||{"phone": "138${num1}", "sms_code": "%{sms_code}", "client_id": "c4", "client_secret": "secret", "grant_type": "sms_code"}||"access_token":||access_token----"access_token":"(\S+?)"||post
login||{"phone": "138${num1}", "sms_code": "", "client_id": "c4", "client_secret": "secret", "grant_type": "sms_code"}||"access_token":||access_token----"access_token":"(\S+?)"||post
login||{"phone": "138${num1}", "sms_code": "%{sms_code}", "client_id": "c4", "client_secret": "", "grant_type": "sms_code"}||"access_token":||access_token----"access_token":"(\S+?)"||post

Pre_data_handler.py 文件:
在 Pre_data_handler.py 文件里面将我们做数据和配置的处理,Pre_data_handler.py 文件包含函数如下:
1. pre_data_handler:处理数据文件 test_data.txt 里面的${}参数部分,将其替换为对应的参数值
2. test_data_post_handler:获取返回结果的关联数据存入global_values 里面
3. after_data_handler:处理数据文件 test_data.txt 里面的%{}参数部分,将其替换为关联值
4. get_test_data:获取 test_data.py 中的测试数据,将获取的每一行数据分隔成 interface(请求接口地址),test_data(请求数据),result_key(验证结果关键字),var_regx(关联处理表达式)和 request_type(请求接口类型)等五部分,然后调用 pre_data_handler 函数处理 test_data 数据里面的参数,最后将这五部分内容,作为测试用例(元组)返回到一个列表中,返回格式为
[(interface,test_data,result_key,var_regx,request_type)…],列表里面的一个元素(元组)就是一个测试用例
注意:test_data.txt 里面的接口必须事先在 server_info.py 定义好

Pre_data_hander.py 代码如下:

# encoding = utf-8
import re
from Server_info import *
from Util import *

global_values = {}


def pre_data_handler(data):
    global global_values

    if re.search(r"\$\{unique_\w+\}", data):  # 匹配用户名参数,即"${www}"的
        var_name = re.search(r"\$\{(unique_\w+)\}", data).group(1)
        var_name = var_name.split("_")[1]
        unique_num = str(get_uniquenumber())  # 获取唯一数
        data = re.sub(r"\$\{unique_\w+\}", unique_num, data)
        global_values[var_name] = str(unique_num)

    if re.search(r"\$\{(\w+)\}", data):  # 将注册的用户名的唯一数取出用于后续用户名拼接
        replaceVarList = re.findall(r"\$\{(\w+)\}", data)
        # print("需要替换的数据: %s" % replaceVarList)
        for var_name in replaceVarList:
            # print("替换前 data:", data)
            data = re.sub(r"\$\{%s\}" % var_name, str(global_values[var_name]), data)
            # print("替换后 data:", data)
    return data


def test_data_post_handler(data, regx):
    # 添加关联
    # print("data: %s" % data)
    global global_values
    if regx.lower().find("None") >= 0:
        return
    var_name = regx.split("----")[0]
    print("var_name: %s" % var_name)
    regx_exp = regx.split("----")[1]
    print("regx_exp: %s" % regx_exp)
    if re.search(regx_exp, data):
        global_values[var_name] = re.search(regx_exp, data).group(1)
        # print("re.search(regx_exp,data).groups(): %s" % re.search(regx_exp,data).groups())
        print("global_values: %s" % global_values)
        return
    return data


def get_test_data(data_file):
    '''读取测试数据文件获取测试数据'''
    test_cases = []
    with open(data_file, 'r') as fp:
        for line in fp:
            line = line.strip()
            interface_name = eval(line.split("||")[0].strip())
            test_data = pre_data_handler(line.split("||")[1].strip())
            result_key = line.split("||")[2].strip()
            var_regx = line.split("||")[3].strip()
            request_type = line.split("||")[4].strip()
            test_cases.append((interface_name, test_data, result_key, var_regx, request_type))
            # print(test_cases)
    return test_cases


def after_data_handler(data):
    '''处理关联数据'''
    global global_values
    # print("global_values: %s" % global_values)
    while re.search(r"\%\{\w+\}", data):
        var_name = re.search(r"\%\{(\w+)\}", data).group(1)
        # print("var_name: %s" % var_name)
        # print("关联替换前 data:", data)
        data = re.sub(r"\%\{(\w+)\}", global_values[var_name], data,
                      1)  # 把 data 中匹配到的参数用 global_values[var_name]替换掉,替换次数是 1
        # print("关联替换后 data:", data)
    return data

Html_report.py 文件:
该文件主要是实现网页格式输出的测试报告,实现的时候使用了 bottle 的第三方模块’ template’来渲染测试报告,生成最终的 html 代码,文件内容如下
Html_report.py:

#encoding=utf-8
from bottle import template
def report_html(data,html_name):

    template_demo = """
    <!-- CSS goes in the document HEAD or added to your external stylesheet -->
<style type="text/css">
table.hovertable {
    font-family: verdana,arial,sans-serif;
    font-size:11px;
    color:#333553;
    border-width: 1px;
    border-color: #999999;
    border-collapse: collapse;
}
table.hovertable th {
    background-color:#00BFFF;
    border-width: 1px;
    padding: 8px;
    border-style: solid;
    border-color: #a9c6c9;
}
table.hovertable tr {
    background-color:#d4e3e5;
}
table.hovertable td {
    border-width: 1px;
    padding: 8px;
    border-style: solid;
    border-color: #a9c6c9;
}
</style>
<!-- Table goes in the document BODY -->
<head>

<meta http-equiv="content-type" content="txt/html; charset=utf-8" />
</head>


<table class="hovertable">
<tr>
    <th>接口 URL</th>
    <th>请求数据</th>
    <th>接口响应数据</th>
    <th>接口调用耗时(单位:ms)</th>
    <th>断言词</th>
    <th>测试结果</th>

</tr>
% for url,request_data,response_data,test_time,assert_word,result,total_test_case, success_test_case,failed_test_case in items:
<tr onmouseover="this.style.backgroundColor='#ffff66';"
onmouseout="this.style.backgroundColor='#d4e3e5';">

<td>{{url}}</td>
<td>{{request_data}}</td>
<td>{{response_data}}</td>
<td>{{test_time}}</td>
<td>{{assert_word}}</td>
<td>
    % if result == '失败':
    <font color=red>
    % end
    {{result}}</td>

</tr>
% end
<tr>
<th>测试总数</th>
<th>测试成功</th>
<th>测试失败</th>
</tr>
<tr>
<td>{{total_test_case}}</td>
<td>{{success_test_case}}</td>
<td>{{failed_test_case}}</td>
</tr>
</table>
    """
    html = template(template_demo, items=data)
    with open(html_name+".html", 'wb') as f:
        f.write(html.encode('utf-8'))

Send_mail.py 文件:
在该文件中,我们使用 python 的发送邮件的包(smtplib),实现了自动发送测试报告邮件
的功能

提示:
使用 python 程序发送邮件时,需要对所使用的邮箱开启一下 POP3/SMTP/IMAP 服务并得到
授权码,方便通过客户端程序连接服务器,服务开启开启方式是在邮箱的设置页面,设置的
时候需要短信验证获取授权码,之后在程序中连接邮箱服务器时用的密码就是所收到的授权
码。各个邮箱服务器设置略有不同,设置的时候网上搜索一下就知道具体的步骤了。
打开 qq 邮箱-设置-账户


send_mail.py:

import os
import smtplib
from email import encoders
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from email.utils import formataddr
def send_mail():
    mail_host="smtp.qq.com" #设置服务器
    mail_user="#你的发送邮箱" #用户名
    mail_pass="#你的发送邮箱口令" #授权码口令
    sender = '#你的发送邮箱'
    receivers = ['#你的接收邮箱'] # 接收邮件,可设置为你的 QQ 邮箱或者其他邮箱
    # 创建一个带附件的实例
    message = MIMEMultipart()
    message['From'] = formataddr(["#发送人", "#你的发送邮箱"])
    message['To'] = ','.join(receivers)
    subject = '接口自动化测试执行报告'
    message['Subject'] = Header(subject, 'utf-8')
    message["Accept-Language"]="zh-CN"
    message["Accept-Charset"]="ISO-8859-1,utf-8,gbk"
    # 邮件正文内容
    message.attach(MIMEText('最新执行的接口自动化测试报告,请参阅附件内容!', 'plain',
    'utf-8'))
    # 构造附件 1,传送测试结果的 html 文件
    att = MIMEBase('application', 'octet-stream')
    att.set_payload(open("接口测试报告.html", 'rb').read())
    att.add_header('Content-Disposition', 'attachment', filename=('utf-8', '', "接口测试报告.html"))
    encoders.encode_base64(att)
    message.attach(att)
    try:
        smtpObj = smtplib.SMTP(mail_host)
        smtpObj.login(mail_user, mail_pass)
        smtpObj.sendmail(sender, receivers, message.as_string())
        print("邮件发送成功")
    except smtplib.SMTPException as e:
        print("Error: 无法发送邮件", e)

if __name__ == '__main__':
    send_mail()

Test_main.py 文件:
主函数文件中调用 get_test_data()方法获取测试用例数据,之后依次遍历所有用例信息,进
行发送请求、验证返回结果等处理,在发送请之前,进行 data 数据处理,如果 data 里面包
含了“%”关联参数关键字,则调用 after_data_handler()方法替换关联参数。
接着处理测试结果、报告,发送邮件。

# encoding = utf-8
from Pre_data_handler import *
from Html_report import report_html
import time
from Send_mail import send_mail

total_test_case = 0  # 记录总共测试用例数
success_test_case = 0  # 记录成功用例数
failed_test_case = 0  # 记录失败用例数
test_cases = get_test_data("test_data.txt")
test_results = []

# 逐一执行用例
for test_cases in test_cases:
    url = test_cases[0]
    data = test_cases[1]
    request_type=test_cases[4]
    test_case_result = ""
    if "%" in data:  # 如果数据中有关联数据
        data = eval(after_data_handler(data))  # 处理关联数据
    result_key = test_cases[2]
    if "getVertificationCode" in url:  # 请求地址是注册,就是json格式,加请求头
        register_headers = {'Content-Type': 'application/json;charset=UTF-8'}
    print(data)
    start_time = time.time()
    re = send_request(url, data, register_headers,request_type)
    end_time = time.time()
    test_time = int((end_time - start_time) * 1000)
    register_headers = None
    total_test_case += 1
    print(re.text)
    if assert_result(re, result_key):
        success_test_case += 1
        test_case_result = "成功"
        print("断言成功")
    else:
        failed_test_case += 1
        test_case_result = "失败"
        print("断言失败")

    try:
        test_data_post_handler(re.text, test_cases[3])
        print("获取关联变量成功")

    except:
        print("从请求结果%s 中,尝试获取关联变量失败,表达式%s" % (re.text, test_cases[3]))
    test_results.append((re.url, data, re.text, test_time, test_cases[2], test_case_result,total_test_case, success_test_case,failed_test_case))

html_name = '接口测试报告'
report_html(test_results, html_name)
send_mail()

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值