Python接口测试实战之搭建自动化测试框架_file=open("

更新: excel表格中,增加一个headers列,内容为json格式, 如下

图片

二.Excel读取方法

Python我们使用三方库xlrd来读取Excel,使用pip install xlrd安装xlrd。

import xlrd

wb = xlrd.open\_workbook("test\_user\_data.xlsx")  # 打开excel
sh = wb.sheet\_by\_name("TestUserLogin")  # 按工作簿名定位工作表
print(sh.nrows)  # 有效数据行数
print(sh.ncols)  # 有效数据列数
print(sh.cell(0, 0).value)  # 输出第一行第一列的值`case_name`
print(sh.row\_values(0))  # 输出第1行的所有值(列表格式)

# 将数据和标题组装成字典,使数据更清晰
print(dict(zip(sh.row\_values(0), sh.row\_values(1))))

# 遍历excel,打印所有的数据
for i in range(sh.nrows):
    print(sh.row\_values(i))

输出结果

3
5
case_name
['case\_name', 'url', 'method', 'data', 'expect\_res']
{'case\_name': 'test\_user\_login\_normal', 'url': 'http://115.28.108.130:5000/api/user/login/', 'method': 'POST', 'data': '{"name": "张三","password":"123456"}', 'expect\_res': '<h1>登录成功</h1>'}
['case\_name', 'url', 'method', 'data', 'expect\_res']
['test\_user\_login\_normal', 'http://115.28.108.130:5000/api/user/login/', 'POST', '{"name": "张三","password":"123456"}', '<h1>登录成功</h1>']
['test\_user\_login\_password\_wrong', 'http://115.28.108.130:5000/api/user/login/', 'POST', '{"name": "张三","password":"1234567"}', '<h1>失败,用户不存在</h1>']

三.封装读取excel操作

1.新建read_excel.py图片
我们的目的是获取某条用例的数据,需要3个参数,excel数据文件名data_file,工作簿名sheet,用例名case_name。如果我们只封装一个函数,每次调用(每条用例)都要打开一次excel并遍历一次,这样效率比较低,我们可以拆分成两个函数,一个函数excel_to_list(data_file, sheet),一次获取一个工作表的所有数据,另一个函数get_test_data(data_list, case_name)从所有数据中去查找到该条用例的数据。

import xlrd

def excel\_to\_list(data_file, sheet):
    data_list = []  # 新建个空列表,来乘装所有的数据
    wb = xlrd.open\_workbook(data_file)  # 打开excel
    sh = wb.sheet\_by\_name(sheet)  # 获取工作簿
    header = sh.row\_values(0)  # 获取标题行数据
    for i in range(1, sh.nrows):  # 跳过标题行,从第二行开始取数据
        d = dict(zip(header, sh.row\_values(i)))  # 将标题和每行数据组装成字典
        data_list.append(d)
    return data_list  # 列表嵌套字典格式,每个元素是一个字典

def get\_test\_data(data_list, case_name):
    for case_data in data_list:
        if case_name == case_data['case\_name']:  # 如果字典数据中case_name与参数一致
            return case_data
            # 如果查询不到会返回None

if __name__ == '\_\_main\_\_':   # 测试一下自己的代码
    data_list = excel\_to\_list("test\_user\_data.xlsx", "TestUserLogin")  # 读取excel,TestUserLogin工作簿的所有数据
    case_data = get\_test\_data(data_list, 'test\_user\_login\_normal')  # 查找用例'test\_user\_login\_normal'的数据
    print(case_data)

输出结果

{'case\_name': 'test\_user\_login\_normal', 'url': 'http://115.28.108.130:5000/api/user/login/', 'method': 'POST', 'data': '{"name": "张三","password":"123456"}', 'expect\_res': '<h1>登录成功</h1>'}

2.用例中使用方法(test_user_login.py)

import unittest
import requests
from read_excel import \*  # 导入read_excel中的方法
import json  # 用来转化excel中的json字符串为字典

class TestUserLogin(unittest.TestCase):
    @classmethod
    def setUpClass(cls):   # 整个测试类只执行一次
        cls.data_list = excel\_to\_list("test\_user\_data.xlsx", "TestUserLogin")  # 读取该测试类所有用例数据
        # cls.data\_list 同 self.data\_list 都是该类的公共属性

    def test\_user\_login\_normal(self):
        case_data = get\_test\_data(self.data_list, 'test\_user\_login\_normal')   # 从数据列表中查找到该用例数据
        if not case_data:   # 有可能为None
            print("用例数据不存在")
        url = case_data.get('url')   # 从字典中取数据,excel中的标题也必须是小写url
        data = case_data.get('data')  # 注意字符串格式,需要用json.loads()转化为字典格式
        expect_res = case_data.get('expect\_res')  # 期望数据

        res = requests.post(url=url, data=json.loads(data))  # 表单请求,数据转为字典格式
        self.assertEqual(res.text, expect_res)  # 改为assertEqual断言

if __name__ == '\_\_main\_\_':   # 非必要,用于测试我们的代码
    unittest.main(verbosity=2)

3.用例中使用方法(test_user_reg.py)

import unittest
import requests
from db import \*
from read_excel import \*
import json

class TestUserReg(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.data_list = excel\_to\_list("test\_user\_data.xlsx", "TestUserReg")  # 读取TestUserReg工作簿的所有数据

    def test\_user\_reg\_normal(self):
        case_data = get\_test\_data(self.data_list, 'test\_user\_reg\_normal')
        if not case_data:
            print("用例数据不存在")
        url = case_data.get('url')
        data = json.loads(case_data.get('data'))  # 转为字典,需要取里面的name进行数据库检查
        expect_res = json.loads(case_data.get('expect\_res'))  # 转为字典,断言时直接断言两个字典是否相等
        name = data.get("name")  # 范冰冰

        # 环境检查
        if check\_user(name):
            del\_user(name)
        # 发送请求
        res = requests.post(url=url, json=data)  # 用data=data 传字符串也可以
        # 响应断言(整体断言)
        self.assertDictEqual(res.json(), expect_res)
        # 数据库断言
        self.assertTrue(check\_user(name))
        # 环境清理(由于注册接口向数据库写入了用户信息)
        del\_user(name)

if __name__ == '\_\_main\_\_':    # 非必要,用于测试我们的代码
    unittest.main(verbosity=2)  

四.增加日志(log)功能

1.新建config.py文件

import logging

logging.basicConfig(level=logging.DEBUG,  # log level
                    format='[%(asctime)s] %(levelname)s [%(funcName)s: %(filename)s, %(lineno)d] %(message)s',  # log格式
                    datefmt='%Y-%m-%d %H:%M:%S',  # 日期格式
                    filename='log.txt',  # 日志输出文件
                    filemode='a')  # 追加模式

if __name__ == '\_\_main\_\_':
    logging.info("hello")

运行后在当前目录下生成log.txt,内容如下

[2018-09-11 18:08:17] INFO [<module>: config.py, 38] hello

2.日志别级(Log Level)

CRITICAL: 用于输出严重错误信息
ERROR: 用于输出错误信息
WARNING: 用于输出警示信息
INFO: 用于输出一些提升信息
DEBUG: 用于输出一些调试信息

3.日志优先级

CRITICAL > ERROR > WARNING > INFO > DEBUG

指定level = logging.DEBUG所有等级大于等于DEBUG的信息都会输出;若指定level = logging.ERROR WARNING,INFO,DEBUG小于设置级别的信息不会输出。

4.日志格式说明

%(levelno)s: 打印日志级别的数值

%(levelname)s: 打印日志级别名称

%(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]

%(filename)s: 打印当前执行程序名

%(funcName)s: 打印日志的当前函数

%(lineno)d: 打印日志的当前行号

%(asctime)s: 打印日志的时间

%(thread)d: 打印线程ID

%(threadName)s: 打印线程名称

%(process)d: 打印进程ID

%(message)s: 打印日志信息

5.日志在项目中的使用

db.py

import pymysql
from config import \*

# 封装数据库查询操作
def query\_db(sql):
    conn = get\_db\_conn()
    cur = conn.cursor()  
    logging.debug(sql)    # 输出执行的sql
    cur.execute(sql)
    conn.commit()
    result = cur.fetchall() 
    logging.debug(result)  # 输出查询结果
    cur.close() 
    conn.close() 
    return result 

# 封装更改数据库操作
def change\_db(sql):
    conn = get\_db\_conn() 
    cur = conn.cursor()
    logging.debug(sql)  # 输出执行的sql
    try:
        cur.execute(sql) 
        conn.commit() 
    except Exception as e:
        conn.rollback() 
        logging.error(str(e))  # 输出错误信息
    finally:
        cur.close() 
        conn.close()

用例

import unittest
import requests
from read_excel import \*  # 导入read_excel中的方法
import json  # 用来转化excel中的json字符串为字典
from config import \*

class TestUserLogin(unittest.TestCase):
    @classmethod
    def setUpClass(cls):   # 整个测试类只执行一次
        cls.data_list = excel\_to\_list("test\_user\_data.xlsx", "TestUserLogin")  # 读取该测试类所有用例数据
        # cls.data\_list 同 self.data\_list 都是该类的公共属性

    def test\_user\_login\_normal(self):
        case_data = get\_test\_data(self.data_list, 'test\_user\_login\_normal')   # 从数据列表中查找到该用例数据
        if not case_data:   # 有可能为None
            logging.error("用例数据不存在")
        url = case_data.get('url')   # excel中的标题也必须是小写url
        data = case_data.get('data')  # 注意字符串格式,需要用json.loads()转化为字典格式
        expect_res = case_data.get('expect\_res')  # 期望数据

        res = requests.post(url=url, data=json.loads(data))  # 表单请求,数据转为字典格式
        logging.info("测试用例:{}".format('test\_user\_login\_normal'))
        logging.info("url:{}".format(url))
        logging.info("请求参数:{}".format(data))
        logging.info("期望结果:{}".format(expect_res))
        logging.info("实际结果:{}".format(res.text)
        self.assertEqual(res.text, expect_res)  # 断言

if __name__ == '\_\_main\_\_':
    unittest.main(verbosity=2)

输出结果

[2018-09-13 10:34:49] INFO [log_case_info: case_log.py, 8] 测试用例:test_user_login_normal
[2018-09-13 10:34:49] INFO [log_case_info: case_log.py, 9] url:http://115.28.108.130:5000/api/user/login/
[2018-09-13 10:34:49] INFO [log_case_info: case_log.py, 10] 请求参数:{"name": "张三","password":"123456"}
[2018-09-13 10:34:49] INFO [log_case_info: case_log.py, 11] 期望结果:<h1>登录成功</h1>
[2018-09-13 10:34:49] INFO [log_case_info: case_log.py, 12] 实际结果:<h1>登录成功</h1>

因为每个用例都要输出很多log信息,我们封装一个case_log的函数

from config import \*
import json

def log\_case\_info(case_name, url, data, expect_res, res_text): 
    if isinstance(data,dict):
        data = json.dumps(data, ensure_ascii=False)  # 如果data是字典格式,转化为字符串
    logging.info("测试用例:{}".format(case_name))
    logging.info("url:{}".format(url))
    logging.info("请求参数:{}".format(data))
    logging.info("期望结果:{}".format(expect_res))
    logging.info("实际结果:{}".format(res_text)

简化后的用例log输出

import unittest
import requests
from read_excel import \*  
import json
from config import \*
from case_log import log_case_info  # 导入方法

class TestUserLogin(unittest.TestCase):
    @classmethod
    def setUpClass(cls):  
        cls.data_list = excel\_to\_list("test\_user\_data.xlsx", "TestUserLogin") 

    def test\_user\_login\_normal(self):
        case_data = get\_test\_data(self.data_list, 'test\_user\_login\_normal') 
        if not case_data: 
            logging.error("用例数据不存在")
        url = case_data.get('url')  
        data = case_data.get('data') 
        expect_res = case_data.get('expect\_res')

        res = requests.post(url=url, data=json.loads(data))
        log\_case\_info('test\_user\_login\_normal', url, data, expect_res, res_text)  # 输出用例log信息
        self.assertEqual(res.text, expect_res)  

if __name__ == '\_\_main\_\_':
    unittest.main(verbosity=2)

如果对python自动化测试、web自动化、接口自动化、移动端自动化、面试经验交流等等感兴趣的测试人,可以 点这自行获取…

五.发送邮件

在生成报告后我们希望框架能自动把报告发送到我们的邮箱中。和outlook,foxmail等邮件客户端一样,Python中发送邮件需要通过Email的smtp服务发送。

1.首先需要确认用来发送邮件的邮箱是否启用了smtp服务;

2.编写邮件内容(Email邮件需要专门的MIME格式);

3.组装Email头(发件人,收件人,主题);

4.连接smtp服务器并发送邮件;

import smtplib  # 用于建立smtp连接
from email.mime.text import MIMEText  # 邮件需要专门的MIME格式

# 1. 编写邮件内容(Email邮件需要专门的MIME格式)
msg = MIMEText('this is a test email', 'plain', 'utf-8')  # plain指普通文本格式邮件内容

# 2. 组装Email头(发件人,收件人,主题)
msg['From'] = 'test\_results@sina.com'  # 发件人
msg['To'] = '2375247815@qq.com'  # 收件人
msg['Subject'] = 'Api Test Report'  # 邮件主题

# 3. 连接smtp服务器并发送邮件
smtp = smtplib.SMTP\_SSL('smtp.sina.com')  # smtp服务器地址 使用SSL模式
smtp.login('自己的邮箱地址', '自己的邮箱密码')  # 用户名和密码
smtp.sendmail("接收邮件地址1", "接收邮件地址2", msg.as\_string())
smtp.quit()

5.中文邮件主题、HTML邮件内容,及附件

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart  # 混合MIME格式,支持上传附件
from email.header import Header  # 用于使用中文邮件主题

# 1.  编写邮件内容
with open('report.html', encoding='utf-8') as f:  # 打开html报告
    email_body = f.read()  # 读取报告内容

msg = MIMEMultipart()  # 混合MIME格式
msg.attach(MIMEText(email_body, 'html', 'utf-8'))  # 添加html格式邮件正文(会丢失css格式)

# 2. 组装Email头(发件人,收件人,主题)
msg['From'] = 'test\_results@sina.com'  # 发件人
msg['To'] = '2375247815@qq.com'  # 收件人
msg['Subject'] = Header('接口测试报告', 'utf-8')  # 中文邮件主题,指定utf-8编码

# 3. 构造附件1,传送当前目录下的 test.txt 文件
att1 = MIMEText(open('report.html', 'rb').read(), 'base64', 'utf-8')  # 二进制格式打开
att1["Content-Type"] = 'application/octet-stream'
att1["Content-Disposition"] = 'attachment; filename="report.html"'  # filename为邮件中附件显示的名字
msg.attach(att1)

# 4. 连接smtp服务器并发送邮件
smtp = smtplib.SMTP\_SSL('smtp.sina.com')  # smtp服务器地址 使用SSL模式
smtp.login('test\_results@sina.com', 'hanzhichao123')  # 用户名和密码
smtp.sendmail("test\_results@sina.com", "2375247815@qq.com", msg.as\_string())
smtp.sendmail("test\_results@sina.com", "superhin@126.com", msg.as\_string())  # 发送给另一个邮箱
smtp.quit()

6.封装发送邮件方法

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart  # 混合MIME格式,支持上传附件
from email.header import Header  # 用于使用中文邮件主题
from config import \*


def send\_email(report_file):
    msg = MIMEMultipart()  # 混合MIME格式
    msg.attach(MIMEText(open(report_file, encoding='utf-8').read(), 'html', 'utf-8'))  # 添加html格式邮件正文(会丢失css格式)



![在这里插入图片描述](https://img-blog.csdnimg.cn/20210511152217670.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poaWd1aWd1,size_16,color_FFFFFF,t_70)

**感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的:**



① 2000多本Python电子书(主流和经典的书籍应该都有了)

② Python标准库资料(最全中文版)

③ 项目源码(四五十个有趣且经典的练手项目及源码)

④ Python基础入门、爬虫、web开发、大数据分析方面的视频(适合小白学习)

⑤ Python学习路线图(告别不入流的学习)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值