Python+unittest+requests+excel实现接口自动化测试框架

  • 环境:python3 + unittest + requests
  • Excel管理测试用例,
  • HTMLTestRunner生成测试报告
  • 测试完成后邮件发送测试报告
  • jsonpath方式做预期结果数据处理,后期多样化处理
  • 后期扩展,CI持续集成

发送邮件效果:
在这里插入图片描述

项目整体结构:
在这里插入图片描述

common模块代码

  1. class IsInstance:

  2. def get_instance(self, value, check):

  3. flag = None

  4. if isinstance(value, str):

  5. if check == value:

  6. flag = True

  7. else:

  8. flag = False

  9. elif isinstance(value, float):

  10. if value - float(check) == 0:

  11. flag = True

  12. else:

  13. flag = False

  14. elif isinstance(value, int):

  15. if value - int(check) == 0:

  16. flag = True

  17. else:

  18. flag = False

  19. return flag

  1. # logger.py

  2. import logging

  3. import time

  4. import os

  5. class MyLogging:

  6. def __init__(self):

  7. timestr = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))

  8. lib_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../logs'))

  9. filename = lib_path + '/' + timestr + '.log' # 日志文件的地址

  10. self.logger = logging.getLogger() # 定义对应的程序模块名name,默认为root

  11. self.logger.setLevel(logging.INFO) # 必须设置,这里如果不显示设置,默认过滤掉warning之前的所有级别的信息

  12. sh = logging.StreamHandler() # 日志输出到屏幕控制台

  13. sh.setLevel(logging.INFO) # 设置日志等级

  14. fh = logging.FileHandler(filename=filename) # 向文件filename输出日志信息

  15. fh.setLevel(logging.INFO) # 设置日志等级

  16. # 设置格式对象

  17. formatter = logging.Formatter(

  18. "%(asctime)s %(filename)s[line:%(lineno)d]%(levelname)s - %(message)s") # 定义日志输出格式

  19. # 设置handler的格式对象

  20. sh.setFormatter(formatter)

  21. fh.setFormatter(formatter)

  22. # 将handler增加到logger中

  23. self.logger.addHandler(sh)

  24. self.logger.addHandler(fh)

  25. if __name__ == "__main__":

  26. log = MyLogging().logger

  27. log.debug("debug")

  28. log.info("info")

  29. log.warning("warning")

  30. log.error("error")

  31. log.critical("critical")

  1. # operate_excel.py

  2. import xlrd

  3. from xlrd import xldate_as_tuple

  4. import openpyxl

  5. import datetime

  6. class ExcelData():

  7. def __init__(self, file_path, sheet_name):

  8. self.file_path = file_path

  9. self.sheet_name = sheet_name

  10. self.workbook = xlrd.open_workbook(self.file_path)

  11. # 获取工作表的内容

  12. self.table = self.workbook.sheet_by_name(self.sheet_name)

  13. # 获取第一行内容

  14. self.keys = self.table.row_values(0)

  15. # 获取行数

  16. self.rowNum = self.table.nrows

  17. # 获取列数

  18. self.colNum = self.table.ncols

  19. def readExcel(self):

  20. datas = []

  21. for i in range(1, self.rowNum):

  22. sheet_data = []

  23. for j in range(self.colNum):

  24. # 获取单元格类型

  25. c_type = self.table.cell(i, j).ctype

  26. # 获取单元格数据

  27. c_cell = self.table.cell_value(i, j)

  28. if c_type == 2 and c_cell % 1 == 0:

  29. c_cell = int(c_cell)

  30. elif c_type == 3:

  31. date = datetime.datetime(*xldate_as_tuple(c_cell, 0))

  32. c_cell = date.strftime('%Y/%d/%m %H:%M:%S')

  33. elif c_type == 4:

  34. c_cell = True if c_cell == 1 else False

  35. # sheet_data[self.keys[j]] = c_cell # 字典

  36. sheet_data.append(c_cell)

  37. datas.append(sheet_data)

  38. return datas

  39. def write(self, rowNum, colNum, result):

  40. workbook = openpyxl.load_workbook(self.file_path)

  41. table = workbook.get_sheet_by_name(self.sheet_name)

  42. table = workbook.active

  43. # rows = table.max_row

  44. # cols = table.max_column

  45. # values = ['E','X','C','E','L']

  46. # for value in values:

  47. # table.cell(rows + 1, 1).value = value

  48. # rows = rows + 1

  49. # 指定单元格中写入数据

  50. table.cell(rowNum, colNum, result)

  51. workbook.save(self.file_path)

  52. if __name__ == '__main__':

  53. file_path = "D:\python_data\接口自动化测试.xlsx"

  54. sheet_name = "测试用例"

  55. data = ExcelData(file_path, sheet_name)

  56. datas = data.readExcel()

  57. print(datas)

  58. print(type(datas))

  59. for i in datas:

  60. print(i)

  61. # data.write(2,12,"哈哈")

  1. # send_email.py

  2. from email.mime.multipart import MIMEMultipart

  3. from email.header import Header

  4. from email.mime.text import MIMEText

  5. from config import read_email_config

  6. import smtplib

  7. def send_email(subject, mail_body, file_names=list()):

  8. # 获取邮件相关信息

  9. smtp_server = read_email_config.smtp_server

  10. port = read_email_config.port

  11. user_name = read_email_config.user_name

  12. password = read_email_config.password

  13. sender = read_email_config.sender

  14. receiver = read_email_config.receiver

  15. # 定义邮件内容

  16. msg = MIMEMultipart()

  17. body = MIMEText(mail_body, _subtype="html", _charset="utf-8")

  18. msg["Subject"] = Header(subject, "utf-8")

  19. msg["From"] = user_name

  20. msg["To"] = receiver

  21. msg.attach(body)

  22. # 附件:附件名称用英文

  23. for file_name in file_names:

  24. att = MIMEText(open(file_name, "rb").read(), "base64", "utf-8")

  25. att["Content-Type"] = "application/octet-stream"

  26. att["Content-Disposition"] = "attachment;filename='%s'" % (file_name)

  27. msg.attach(att)

  28. # 登录并发送邮件

  29. try:

  30. smtp = smtplib.SMTP()

  31. smtp.connect(smtp_server)

  32. smtp.login(user_name, password)

  33. smtp.sendmail(sender, receiver.split(','), msg.as_string())

  34. except Exception as e:

  35. print(e)

  36. print("邮件发送失败!")

  37. else:

  38. print("邮件发送成功!")

  39. finally:

  40. smtp.quit()

  41. if __name__ == '__main__':

  42. subject = "测试标题"

  43. mail_body = "测试本文"

  44. receiver = "780156051@qq.com,hb_zhijun@163.com" # 接收人邮件地址 用逗号分隔

  45. file_names = [r'D:\PycharmProjects\AutoTest\result\2020-02-23 13_38_41report.html']

  46. send_email(subject, mail_body, receiver, file_names)

  1. # send_request.py

  2. import requests

  3. import json

  4. class RunMethod:

  5. # post请求

  6. def do_post(self, url, data, headers=None):

  7. res = None

  8. if headers != None:

  9. res = requests.post(url=url, json=data, headers=headers)

  10. else:

  11. res = requests.post(url=url, json=data)

  12. return res.json()

  13. # get请求

  14. def do_get(self, url, data=None, headers=None):

  15. res = None

  16. if headers != None:

  17. res = requests.get(url=url, data=data, headers=headers)

  18. else:

  19. res = requests.get(url=url, data=data)

  20. return res.json()

  21. def run_method(self, method, url, data=None, headers=None):

  22. res = None

  23. if method == "POST" or method == "post":

  24. res = self.do_post(url, data, headers)

  25. else:

  26. res = self.do_get(url, data, headers)

  27. return res

config模块

  1. # coding:utf-8

  2. # 邮件配置信息

  3. [mysqlconf]

  4. host = 127.0.0.1

  5. port = 3306

  6. user = root

  7. password = root

  8. db = test

  9. # coding:utf-8

  10. # 邮箱配置信息

  11. # email_config.ini

  12. [email]

  13. smtp_server = smtp.qq.com

  14. port = 465

  15. sender = 780***51@qq.com

  16. password = hrpk******baf

  17. user_name = 780***51@qq.com

  18. receiver = 780***51@qq.com,h***n@163.com

  19. # coding:utf-8

  20. from pymysql import connect, cursors

  21. from pymysql.err import OperationalError

  22. import os

  23. import configparser

  24. # read_db_config.py

  25. # 读取DB配数据

  26. # os.path.realpath(__file__):返回当前文件的绝对路径

  27. # os.path.dirname(): 返回()所在目录

  28. cur_path = os.path.dirname(os.path.realpath(__file__))

  29. configPath = os.path.join(cur_path, "db_config.ini") # 路径拼接:/config/db_config.ini

  30. conf = configparser.ConfigParser()

  31. conf.read(configPath, encoding="UTF-8")

  32. host = conf.get("mysqlconf", "host")

  33. port = conf.get("mysqlconf", "port ")

  34. user = conf.get("mysqlconf", "user")

  35. password = conf.get("mysqlconf", "password")

  36. port = conf.get("mysqlconf", "port")

  1. # coding:utf-8

  2. import os

  3. import configparser

  4. # 读取邮件数据

  5. # os.path.realpath(__file__):返回当前文件的绝对路径

  6. # os.path.dirname(): 返回()所在目录

  7. # read_email_config.py

  8. cur_path = os.path.dirname(os.path.realpath(__file__)) # 当前文件的所在目录

  9. configPath = os.path.join(cur_path, "email_config.ini") # 路径拼接:/config/email_config.ini

  10. conf = configparser.ConfigParser()

  11. conf.read(configPath, encoding='UTF-8') # 读取/config/email_config.ini 的内容

  12. # get(section,option) 得到section中option的值,返回为string类型

  13. smtp_server = conf.get("email", "smtp_server")

  14. sender = conf.get("email", "sender")

  15. user_name = conf.get("email","user_name")

  16. password = conf.get("email", "password")

  17. receiver = conf.get("email", "receiver")

  18. port = conf.get("email", "port")

testcase模块

  1. # test_case.py

  2. from common.operate_excel import *

  3. import unittest

  4. from parameterized import parameterized

  5. from common.send_request import RunMethod

  6. import json

  7. from common.logger import MyLogging

  8. import jsonpath

  9. from common.is_instance import IsInstance

  10. from HTMLTestRunner import HTMLTestRunner

  11. import os

  12. import time

  13. lib_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../data"))

  14. file_path = lib_path + "/" + "接口自动化测试.xlsx" # excel的地址

  15. sheet_name = "测试用例"

  16. log = MyLogging().logger

  17. def getExcelData():

  18. list = ExcelData(file_path, sheet_name).readExcel()

  19. return list

  20. class TestCase(unittest.TestCase):

  21. @parameterized.expand(getExcelData())

  22. def test_api(self, rowNumber, caseRowNumber, testCaseName, priority, apiName, url, method, parmsType, data,

  23. checkPoint, isRun, result):

  24. if isRun == "Y" or isRun == "y":

  25. log.info("【开始执行测试用例:{}】".format(testCaseName))

  26. headers = {"Content-Type": "application/json"}

  27. data = json.loads(data) # 字典对象转换为json字符串

  28. c = checkPoint.split(",")

  29. log.info("用例设置检查点:%s" % c)

  30. print("用例设置检查点:%s" % c)

  31. log.info("请求url:%s" % url)

  32. log.info("请求参数:%s" % data)

  33. r = RunMethod()

  34. res = r.run_method(method, url, data, headers)

  35. log.info("返回结果:%s" % res)

  36. flag = None

  37. for i in range(0, len(c)):

  38. checkPoint_dict = {}

  39. checkPoint_dict[c[i].split('=')[0]] = c[i].split('=')[1]

  40. # jsonpath方式获取检查点对应的返回数据

  41. list = jsonpath.jsonpath(res, c[i].split('=')[0])

  42. value = list[0]

  43. check = checkPoint_dict[c[i].split('=')[0]]

  44. log.info("检查点数据{}:{},返回数据:{}".format(i + 1, check, value))

  45. print("检查点数据{}:{},返回数据:{}".format(i + 1, check, value))

  46. # 判断检查点数据是否与返回的数据一致

  47. flag = IsInstance().get_instance(value, check)

  48. if flag:

  49. log.info("【测试结果:通过】")

  50. ExcelData(file_path, sheet_name).write(rowNumber + 1, 12, "Pass")

  51. else:

  52. log.info("【测试结果:失败】")

  53. ExcelData(file_path, sheet_name).write(rowNumber + 1, 12, "Fail")

  54. # 断言

  55. self.assertTrue(flag, msg="检查点数据与实际返回数据不一致")

  56. else:

  57. unittest.skip("不执行")

  58. if __name__ == '__main__':

  59. # unittest.main()

  60. # Alt+Shift+f10 执行生成报告

  61. # 报告样式1

  62. suite = unittest.TestSuite()

  63. suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCase))

  64. now = time.strftime('%Y-%m-%d %H_%M_%S')

  65. report_path = r"D:\PycharmProjects\AutoTest\result\report.html"

  66. with open(report_path, "wb") as f:

  67. runner = HTMLTestRunner(stream=f, title="Esearch接口测试报告", description="测试用例执行情况", verbosity=2)

  68. runner.run(suite)

用例执行文件

  1. import os

  2. import time

  3. import unittest

  4. from HTMLTestRunner import HTMLTestRunner

  5. from common.send_email import send_email

  6. # run_case.py

  7. # 获取当前py文件绝对路径

  8. cur_path = os.path.dirname(os.path.realpath(__file__))

  9. # 1: 加载测试用例

  10. def all_test():

  11. case_path = os.path.join(cur_path, "testcase")

  12. suite = unittest.TestLoader().discover(start_dir=case_path, pattern="test_*.py", top_level_dir=None)

  13. return suite

  14. # 2: 执行测试用例

  15. def run():

  16. now = time.strftime("%Y_%m_%d_%H_%M_%S")

  17. # 测试报告路径

  18. file_name = os.path.join(cur_path, "report") + "/" + now + "-report.html"

  19. f = open(file_name, "wb")

  20. runner = HTMLTestRunner(stream=f, title="接口自动化测试报告",

  21. description="环境:windows 10 浏览器:chrome",

  22. tester="wangzhijun")

  23. runner.run(all_test())

  24. f.close()

  25. # 3: 获取最新的测试报告

  26. def get_report(report_path):

  27. list = os.listdir(report_path)

  28. list.sort(key=lambda x: os.path.getmtime(os.path.join(report_path, x)))

  29. print("测试报告:", list[-1])

  30. report_file = os.path.join(report_path, list[-1])

  31. return report_file

  32. # 4: 发送邮件

  33. def send_mail(subject, report_file, file_names):

  34. # 读取测试报告内容,作为邮件的正文内容

  35. with open(report_file, "rb") as f:

  36. mail_body = f.read()

  37. send_email(subject, mail_body, file_names)

  38. if __name__ == "__main__":

  39. run()

  40. report_path = os.path.join(cur_path, "report") # 测试报告路径

  41. report_file = get_report(report_path) # 测试报告文件

  42. subject = "Esearch接口测试报告" # 邮件主题

  43. file_names = [report_file] # 邮件附件

  44. # 发送邮件

  45. send_mail(subject, report_file, file_names)

data:

在这里插入图片描述

report:
在这里插入图片描述

logs:

在这里插入图片描述

 

总结:

感谢每一个认真阅读我文章的人!!!

作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

 

          视频文档获取方式:
这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享,点下方小卡片即可自行领取。

  • 17
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值