• Unittest接口自动化测试


    我又来了,来分享年前的unittest接口自动化实战啦。这次自动化接口框架比较简单,但是五脏俱全。(注:项目是针对我们公司内部系统的测试,我就不分享链接了。)

    项目简介

    项目名称:****名片系统

    项目目的:实现系统项目自动化测试执行

    项目版本:v1.0

    项目目录

    1

    2

    3

    4

    5

    6

    tools  #存放辅助方法   configEmail.py   #发送测试报告电子邮件

      HTMLTestRunner.py #第三方插件

      log.py #输出日志文件

      mail_receiver.txt #存放接收人邮箱地址

      read_json.py #读取单一测试数据json文件

      read_more_json #读取more测试数据json文件report #存放html测试报告logs  #存放输入日志文件data  #存放参数化测试数据(json文件)case  #存放测试用例api   #存放封装测试方法

     

     

     

    caselist.txt    #存放要执行的测试用例

    getpathInfo.py    #获取当前路径

    runAll.py    #运行caselist.txt中的测试用例,输出测试报告 

    项目框架

    unittest单元测试框架

    项目设计

    1.每一个用例组合在一个测试类里面生成一个py文件

    2.一个模块(被测项目功能)对应一个py文件及一个测试类(测试文件)

    3.通过 parameterized 对参数进行参数化

    项目目标

    1. 生成测试用例执行结果报告

    2.生成测试用例执行日志

    3.用例执行失败或者执行完成后自动发送邮件报告

    4.数据驱动(读取测试数据,减少脚本维护成本)

    项目代码

      getpathInfo.py   #获取当前路径

    1. import os
    2. def get_Path():
    3. #获取上级路径
    4. #path = os.path.abspath(os.path.join(os.getcwd(), ".."))
    5. #获取当前路径
    6. path = os.path.split(os.path.realpath(__file__))[0]
    7. return path
    8. if __name__ == '__main__':# 执行该文件,测试下是否OK
    9. print('测试路径是否OK,路径为:', get_Path())

      caselist.txt  #存放可执行测试用例  runAll.py   #运行文件

    1. import os
    2. import time
    3. from BeautifulReport import BeautifulReport
    4. import getpathInfo
    5. import tools.HTMLTestRunner as HTMLTestRunner
    6. import unittest
    7. from tools.configEmail import send_email, getReceiverInfo
    8. import tools.Log
    9. path = getpathInfo.get_Path()
    10. report_path = os.path.join(path, 'report')
    11. log = tools.Log.logger
    12. class AllTest:#定义一个类AllTest
    13. def __init__(self):#初始化一些参数和数据
    14. global resultPath
    15. resultPath = os.path.join(report_path)
    16. self.caseListFile = os.path.join(path, "caselist.txt")#配置执行哪些测试文件的配置文件路径
    17. self.caseFile = os.path.join(path, "case")#真正的测试断言文件路径
    18. self.caseList = []
    19. log.info('resultPath'+resultPath)#将resultPath的值输入到日志,方便定位查看问题
    20. log.info('caseListFile'+self.caseListFile)#同理
    21. log.info('caseList'+str(self.caseList))#同理
    22. def set_case_list(self):
    23. """
    24. 读取caselist.txt文件中的用例名称,并添加到caselist元素组
    25. :return:
    26. """
    27. fb = open(self.caseListFile)
    28. for value in fb.readlines():
    29. data = str(value)
    30. if data != '' and not data.startswith("#"):# 如果data非空且不以#开头
    31. self.caseList.append(data.replace("\n", ""))#读取每行数据会将换行转换为\n,去掉每行数据中的\n
    32. fb.close()
    33. def set_case_suite(self):
    34. """
    35. :return:
    36. """
    37. self.set_case_list()#通过set_case_list()拿到caselist元素组
    38. test_suite = unittest.TestSuite()
    39. suite_module = []
    40. for case in self.caseList:#从caselist元素组中循环取出case
    41. case_name = case.split("/")[-1]#通过split函数来将aaa/bbb分割字符串,-1取后面,0取前面
    42. print(case_name+".py")#打印出取出来的名称
    43. #批量加载用例,第一个参数为用例存放路径,第一个参数为路径文件名
    44. discover = unittest.defaultTestLoader.discover(self.caseFile, pattern=case_name + '.py', top_level_dir=None)
    45. suite_module.append(discover)#将discover存入suite_module元素组
    46. print('suite_module:'+str(suite_module))
    47. if len(suite_module) > 0:#判断suite_module元素组是否存在元素
    48. for suite in suite_module:#如果存在,循环取出元素组内容,命名为suite
    49. for test_name in suite:#从discover中取出test_name,使用addTest添加到测试集
    50. test_suite.addTest(test_name)
    51. else:
    52. print('else:')
    53. return None
    54. return test_suite#返回测试集
    55. def run(self):
    56. """
    57. run test
    58. :return:
    59. """
    60. try:
    61. suit = self.set_case_suite()#调用set_case_suite获取test_suite
    62. print('try')
    63. print(str(suit))
    64. if suit is not None:#判断test_suite是否为空
    65. print('if-suit')
    66. currTime = time.strftime('%Y-%m-%d %H_%M_%S')
    67. filename = currTime + '.html'
    68. # currTime = time.strftime('%Y-%m-%d %H_%M_%S')
    69. # fileName = report_path + r'\report'+ currTime + '.html'
    70. result = BeautifulReport(suit)
    71. result.report(filename= filename, description='接口测试报告')
    72. # fp = open(fileName, 'wb')
    73. # runner = HTMLTestRunner.HTMLTestReportCN \
    74. # (stream=fp, title='自动化接口测试报告',
    75. # description='处理器:Intel(R) Core(TM) '
    76. # 'i5-5200U CPU @ 2.20GHz 2.20 GHz '
    77. # '内存:8G 系统类型: 64位 版本: windows 10 专业版')
    78. # runner.run(suit)
    79. else:
    80. print("Have no case to test.")
    81. except Exception as ex:
    82. print(str(ex))
    83. #log.info(str(ex))
    84. finally:
    85. print("*********TEST END*********")
    86. #log.info("*********TEST END*********")
    87. #fp.close()
    88. #发送测试邮件
    89. # read_msg = getReceiverInfo(
    90. # r'F:\python_test\Automation_interfaceTest\tools\mail_receiver.txt')
    91. # sendmail = send_email(read_msg)
    92. # sendmail.sendEmail(fileName)
    93. # pythoncom.CoInitialize()
    94. # scheduler = BlockingScheduler()
    95. # scheduler.add_job(AllTest().run, 'cron', day_of_week='1-5', hour=14, minute=59)
    96. # scheduler.start()
    97. if __name__ == '__main__':
    98. AllTest().run()

      tools   #辅助方法  configEmail.py  # 发送邮件

    1. import os
    2. import smtplib
    3. import getpathInfo
    4. from tools.Log import Logger
    5. from email.mime.text import MIMEText
    6. from email.header import Header
    7. log = Logger(__name__)
    8. path = getpathInfo.get_Path()
    9. report_path = os.path.join(path, 'report') # 存放测试报告文件的路径
    10. mail_path = os.path.join(path,'tools')#存放收件人地址文件路径
    11. class send_email():
    12. '''
    13. 邮件配置信息
    14. '''
    15. def __init__(self,
    16. receiver,
    17. subject='*******',
    18. server='smtp.qq.com',
    19. fromuser='******',
    20. frompassword='yjkxwfmrbumrbbce',
    21. sender='*******'):
    22. """
    23. :param receiver:
    24. :param subject:
    25. :param server:
    26. :param fromuser:
    27. :param frompassword:
    28. :param sender:
    29. """
    30. self._server = server
    31. self._fromuser = fromuser
    32. self._frompassword = frompassword
    33. self._sender = sender
    34. self._receiver = receiver
    35. self._subject = subject
    36. def sendEmail(self, fileName):
    37. """
    38. :param filename:
    39. :return:
    40. """
    41. # 打开报告文件读取文件内容
    42. try:
    43. f = open(os.path.join(report_path, fileName), 'rb')
    44. fileMsg = f.read()
    45. except Exception:
    46. log.logger.exception(
    47. 'open or read file [%s] failed,No such file or directory: %s' % (fileName, report_path))
    48. log.logger.info('open and read file [%s] successed!' % fileName)
    49. else:
    50. f.close()
    51. # 邮件主题
    52. subject = 'Python test report' #
    53. # 邮件设置
    54. msg = MIMEText(fileMsg, 'html', 'utf-8')
    55. msg['subject'] = Header(subject, 'utf-8')
    56. msg['from'] = self._sender
    57. # 连接服务器,登录服务器,发送邮件
    58. try:
    59. smtp = smtplib.SMTP()
    60. smtp.connect(self._server)
    61. smtp.login(self._fromuser, self._frompassword)
    62. except Exception:
    63. log.logger.exception('connect [%s] server failed or username and password incorrect!' % smtp)
    64. else:
    65. log.logger.info('email server [%s] login success!' % smtp)
    66. try:
    67. smtp.sendmail(self._sender, self._receiver, msg.as_string())
    68. except Exception:
    69. log.logger.exception('send email failed!')
    70. else:
    71. log.logger.info('send email successed!')
    72. # 从文件中读取邮件接收人信息
    73. def getReceiverInfo(fileName):
    74. '''
    75. :param filename: 读取接收邮件人信息
    76. :return: 接收邮件人信息
    77. '''
    78. try:
    79. openFile = open(os.path.join(mail_path, fileName))
    80. except Exception:
    81. log.logger.exception('open or read file [%s] failed,No such file or directory: %s' % (fileName, mail_path))
    82. else:
    83. log.logger.info('open file [%s] successed!' % fileName)
    84. for line in openFile:
    85. msg = [i.strip() for i in line.split(',')]
    86. log.logger.info('reading [%s] and got receiver value is [%s]' % (fileName, msg))
    87. return msg
    88. if __name__ == '__main__':# 运营此文件来验证写的send_email是否正确
    89. readMsg = getReceiverInfo('mail_receiver.txt')
    90. sendmail = send_email(readMsg)
    91. sendmail.sendEmail('report.html')

    1

    HTMLTestRunner.py #第三方插件
    log.py #输出日志文件

    1. import os
    2. import logging
    3. import time
    4. from logging.handlers import TimedRotatingFileHandler
    5. import getpathInfo
    6. path = getpathInfo.get_Path()
    7. log_path = os.path.join(path, 'logs') # 存放log文件的路径
    8. class Logger(object):
    9. def __init__(self, logger_name='logs…'):
    10. self.logger = logging.getLogger(logger_name)
    11. logging.root.setLevel(logging.NOTSET)
    12. currTime = time.strftime("%Y-%m-%d")
    13. self.log_file_name = currTime+'logs' # 日志文件的名称
    14. self.backup_count = 5 # 最多存放日志的数量
    15. # 日志输出级别
    16. self.console_output_level = 'WARNING'
    17. self.file_output_level = 'DEBUG'
    18. # 日志输出格式
    19. self.formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    20. def get_logger(self):
    21. """在logger中添加日志句柄并返回,如果logger已有句柄,则直接返回"""
    22. if not self.logger.handlers: # 避免重复日志
    23. console_handler = logging.StreamHandler()
    24. console_handler.setFormatter(self.formatter)
    25. console_handler.setLevel(self.console_output_level)
    26. self.logger.addHandler(console_handler)
    27. # 每天重新创建一个日志文件,最多保留backup_count
    28. file_handler = TimedRotatingFileHandler(filename=os.path.join(log_path, self.log_file_name), when='D',
    29. interval=1, backupCount=self.backup_count, delay=True,
    30. encoding='utf-8')
    31. file_handler.setFormatter(self.formatter)
    32. file_handler.setLevel(self.file_output_level)
    33. self.logger.addHandler(file_handler)
    34. return self.logger
    35. logger = Logger().get_logger()

     mail_receiver.txt  #存放接受人邮箱地址 read_json.py   # 读取单一测试数据 

    1. import json
    2. import getpathInfo
    3. import os
    4. class ReadJson(object):
    5. def __init__(self,filename):
    6. path = getpathInfo.get_Path()
    7. self.filepath = os.path.join(path, 'data')+"/"+filename
    8. def read_json(self):
    9. with open(self.filepath, "r", encoding="utf-8")as f:
    10. # 调用load方法加载文件流
    11. return json.load(f)
    12. if __name__ == '__main__':
    13. data = ReadJson("updateuserpwd.json").read_json()
    14. arrs = []
    15. arrs.append((data.get("url"),
    16. data.get("userId"),
    17. data.get("data"),
    18. data.get("success"),
    19. data.get("message")))
    20. print(arrs)

     read_more_json.py   # 读取more测试数据 

    1. import json
    2. class ReadJson(object):
    3. def __init__(self,filename):
    4. self.filepath = '../data/' +filename
    5. def read_json(self):
    6. with open(self.filepath, "r", encoding="utf-8")as f:
    7. # 调用load方法加载文件流
    8. return json.load(f)
    9. if __name__ == '__main__':
    10. datas = ReadJson("login_more.json").read_json()
    11. arrs = []
    12. for data in datas.values():
    13. arrs.append((data.get("url"),
    14. data.get("mobile"),
    15. data.get("code"),
    16. data.get("expect_result"),
    17. data.get("status_code")))
    18. print(arrs)

     #测试用例

    1. import requests
    2. class ApiLogin(object):
    3. def api_post_login(self,data):
    4. #headers定义
    5. headers = {"Content-Type": "application/x-www-form-urlencoded"}
    6. #url定义
    7. url = "http://*******"
    8. #调用post返回相应对象
    9. return requests.post(url, headers=headers,data = data)

    1. #客户经理登录接口
    2. import sys
    3. import unittest
    4. from api.api_login import ApiLogin
    5. from parameterized import parameterized
    6. from tools.read_more_json import ReadJson
    7. import tools.Log
    8. log = tools.Log.logger
    9. #读取数据函数
    10. def get_data():
    11. datas = ReadJson('login.json').read_json()
    12. arrs = []
    13. for data in datas.values():
    14. arrs.append((data.get("data"),
    15. data.get("message"),
    16. data.get("desc")))
    17. return arrs
    18. class TestLogin(unittest.TestCase):
    19. '''登录接口 '''
    20. @parameterized.expand(get_data()) # 参数化测试用例
    21. def test_login(self,data,message,desc):
    22. #调用登录方法
    23. s = ApiLogin().api_post_login(data)
    24. #调试使用添加描述
    25. self._testMethodDoc = desc
    26. #print('查看响应结果:',s.text)
    27. #断言响应信息
    28. self.assertIn(message,s.text)
    29. # 生成响应日志
    30. log.info('[%s]响应数据为:[%s]' % (sys._getframe().f_code.co_name, s.text))
    31. if __name__ == '__main__':
    32. unittest.main()

    #测试数据

    1. {
    2. "login_001":{
    3. "data":{"loginName":"admin", "password": "123456","memberPass": "on"},
    4. "message": "名片管理系统",
    5. "desc": "正常登录"},
    6. "login_002": {
    7. "data":{"loginName":"admin", "password": "123456n","memberPass": "on"},
    8. "message": "密码不正确",
    9. "desc": "账号错误登录"},
    10. "login_003": {
    11. "data":{"loginName":"admir", "password": "123456","memberPass": "on"},
    12. "message": "账号不存在",
    13. "desc": "密码错误登录"},
    14. "login_004": {
    15. "data":{"loginName":" ", "password": "123456","memberPass": "on"},
    16. "message": "帐号密码登录",
    17. "desc": "密码为空登录"},
    18. "login_005": {
    19. "data":{"loginName":"admin", "password": " ","memberPass": "on"},
    20. "message": "密码不正确",
    21. "desc":"账号为空登录"},
    22. "login_006": {
    23. "data":{"loginName":"adm in", "password": "123456","memberPass": "on"},
    24. "message": "账号不存在",
    25. "desc":"账号存在空格登录"},
    26. "login_007": {
    27. "data":{"loginName":"admin", "password": "123 456","memberPass": "on"},
    28. "message": "密码不正确",
    29. "desc":"密码存在空格登录"},
    30. "login_008": {
    31. "data":{"loginName":"admin.", "password": "123456","memberPass": "on"},
    32. "message": "账号不存在",
    33. "desc":"账号存在特殊符号登录"},
    34. "login_009": {
    35. "data":{"loginName":"admin", "password": "123456.","memberPass": "on"},
    36. "message": "密码不正确",
    37. "desc":"密码存在特殊符号登录"},
    38. "login_010": {
    39. "data":{"loginName":"admi", "password": "123456","memberPass": "on"},
    40. "message": "账号不存在",
    41. "desc":"账号不完整登录"},
    42. "login_010": {
    43. "data":{"loginName":"admin", "password": "12345","memberPass": "on"},
    44. "message": "密码不正确",
    45. "desc":"密码不完整登录"}
    46. }

     #测试报告

     就这样吧,其他测试用例与之类似就不展出了。

  • 相关阅读:
    【目标检测】YOLOv5:标签中文显示/自定义颜色
    用python实现自动回复QQ消息——不到60行
    计算机毕设 大数据全国疫情数据分析与3D可视化 - python 大数据
    python如何判断字符串中的所有字符都是字母——isalpha函数的用法及实例
    第12章 最佳的UI体验——Material Design实战
    Oracle/PLSQL: To_Timestamp_Tz Function
    flex_dashboard
    时序预测 | MATLAB实现EMD-iCHOA+GRU基于经验模态分解-改进黑猩猩算法优化门控循环单元的时间序列预测
    Hermes - 指尖上的智慧:自定义问答系统的崭新世界
    mac 升级node到指定版本
  • 原文地址:https://blog.csdn.net/wqda125/article/details/133643943