我又来了,来分享年前的unittest接口自动化实战啦。这次自动化接口框架比较简单,但是五脏俱全。(注:项目是针对我们公司内部系统的测试,我就不分享链接了。)
项目名称:****名片系统
项目目的:实现系统项目自动化测试执行
项目版本:v1.0
| 1 2 3 4 5 6 |
|





caselist.txt #存放要执行的测试用例
getpathInfo.py #获取当前路径
runAll.py #运行caselist.txt中的测试用例,输出测试报告
unittest单元测试框架
1.每一个用例组合在一个测试类里面生成一个py文件
2.一个模块(被测项目功能)对应一个py文件及一个测试类(测试文件)
3.通过 parameterized 对参数进行参数化
1. 生成测试用例执行结果报告
2.生成测试用例执行日志
3.用例执行失败或者执行完成后自动发送邮件报告
4.数据驱动(读取测试数据,减少脚本维护成本)
getpathInfo.py #获取当前路径
- import os
-
- def get_Path():
- #获取上级路径
- #path = os.path.abspath(os.path.join(os.getcwd(), ".."))
- #获取当前路径
- path = os.path.split(os.path.realpath(__file__))[0]
- return path
-
- if __name__ == '__main__':# 执行该文件,测试下是否OK
- print('测试路径是否OK,路径为:', get_Path())
caselist.txt #存放可执行测试用例 runAll.py #运行文件
-
- import os
- import time
-
- from BeautifulReport import BeautifulReport
-
- import getpathInfo
- import tools.HTMLTestRunner as HTMLTestRunner
- import unittest
- from tools.configEmail import send_email, getReceiverInfo
- import tools.Log
-
- path = getpathInfo.get_Path()
- report_path = os.path.join(path, 'report')
- log = tools.Log.logger
-
- class AllTest:#定义一个类AllTest
- def __init__(self):#初始化一些参数和数据
- global resultPath
- resultPath = os.path.join(report_path)
- self.caseListFile = os.path.join(path, "caselist.txt")#配置执行哪些测试文件的配置文件路径
- self.caseFile = os.path.join(path, "case")#真正的测试断言文件路径
- self.caseList = []
- log.info('resultPath'+resultPath)#将resultPath的值输入到日志,方便定位查看问题
- log.info('caseListFile'+self.caseListFile)#同理
- log.info('caseList'+str(self.caseList))#同理
-
- def set_case_list(self):
- """
- 读取caselist.txt文件中的用例名称,并添加到caselist元素组
- :return:
- """
- fb = open(self.caseListFile)
- for value in fb.readlines():
- data = str(value)
- if data != '' and not data.startswith("#"):# 如果data非空且不以#开头
- self.caseList.append(data.replace("\n", ""))#读取每行数据会将换行转换为\n,去掉每行数据中的\n
- fb.close()
-
- def set_case_suite(self):
- """
- :return:
- """
- self.set_case_list()#通过set_case_list()拿到caselist元素组
- test_suite = unittest.TestSuite()
- suite_module = []
- for case in self.caseList:#从caselist元素组中循环取出case
- case_name = case.split("/")[-1]#通过split函数来将aaa/bbb分割字符串,-1取后面,0取前面
- print(case_name+".py")#打印出取出来的名称
- #批量加载用例,第一个参数为用例存放路径,第一个参数为路径文件名
- discover = unittest.defaultTestLoader.discover(self.caseFile, pattern=case_name + '.py', top_level_dir=None)
- suite_module.append(discover)#将discover存入suite_module元素组
- print('suite_module:'+str(suite_module))
- if len(suite_module) > 0:#判断suite_module元素组是否存在元素
- for suite in suite_module:#如果存在,循环取出元素组内容,命名为suite
- for test_name in suite:#从discover中取出test_name,使用addTest添加到测试集
- test_suite.addTest(test_name)
- else:
- print('else:')
- return None
- return test_suite#返回测试集
-
- def run(self):
- """
- run test
- :return:
- """
- try:
- suit = self.set_case_suite()#调用set_case_suite获取test_suite
- print('try')
- print(str(suit))
- if suit is not None:#判断test_suite是否为空
- print('if-suit')
- currTime = time.strftime('%Y-%m-%d %H_%M_%S')
- filename = currTime + '.html'
- # currTime = time.strftime('%Y-%m-%d %H_%M_%S')
- # fileName = report_path + r'\report'+ currTime + '.html'
- result = BeautifulReport(suit)
- result.report(filename= filename, description='接口测试报告')
- # fp = open(fileName, 'wb')
- # runner = HTMLTestRunner.HTMLTestReportCN \
- # (stream=fp, title='自动化接口测试报告',
- # description='处理器:Intel(R) Core(TM) '
- # 'i5-5200U CPU @ 2.20GHz 2.20 GHz '
- # '内存:8G 系统类型: 64位 版本: windows 10 专业版')
- # runner.run(suit)
- else:
- print("Have no case to test.")
- except Exception as ex:
- print(str(ex))
- #log.info(str(ex))
-
- finally:
- print("*********TEST END*********")
- #log.info("*********TEST END*********")
- #fp.close()
- #发送测试邮件
- # read_msg = getReceiverInfo(
- # r'F:\python_test\Automation_interfaceTest\tools\mail_receiver.txt')
- # sendmail = send_email(read_msg)
- # sendmail.sendEmail(fileName)
- # pythoncom.CoInitialize()
- # scheduler = BlockingScheduler()
- # scheduler.add_job(AllTest().run, 'cron', day_of_week='1-5', hour=14, minute=59)
- # scheduler.start()
-
- if __name__ == '__main__':
- AllTest().run()
tools #辅助方法 configEmail.py # 发送邮件
-
- import os
- import smtplib
-
-
- import getpathInfo
- from tools.Log import Logger
- from email.mime.text import MIMEText
- from email.header import Header
- log = Logger(__name__)
- path = getpathInfo.get_Path()
- report_path = os.path.join(path, 'report') # 存放测试报告文件的路径
- mail_path = os.path.join(path,'tools')#存放收件人地址文件路径
- class send_email():
- '''
- 邮件配置信息
- '''
-
- def __init__(self,
- receiver,
- subject='*******',
- server='smtp.qq.com',
- fromuser='******',
- frompassword='yjkxwfmrbumrbbce',
- sender='*******'):
- """
- :param receiver:
- :param subject:
- :param server:
- :param fromuser:
- :param frompassword:
- :param sender:
- """
-
- self._server = server
- self._fromuser = fromuser
- self._frompassword = frompassword
- self._sender = sender
- self._receiver = receiver
- self._subject = subject
-
- def sendEmail(self, fileName):
- """
- :param filename:
- :return:
- """
- # 打开报告文件读取文件内容
- try:
- f = open(os.path.join(report_path, fileName), 'rb')
- fileMsg = f.read()
- except Exception:
- log.logger.exception(
- 'open or read file [%s] failed,No such file or directory: %s' % (fileName, report_path))
- log.logger.info('open and read file [%s] successed!' % fileName)
- else:
- f.close()
- # 邮件主题
- subject = 'Python test report' #
- # 邮件设置
- msg = MIMEText(fileMsg, 'html', 'utf-8')
- msg['subject'] = Header(subject, 'utf-8')
- msg['from'] = self._sender
- # 连接服务器,登录服务器,发送邮件
- try:
- smtp = smtplib.SMTP()
- smtp.connect(self._server)
- smtp.login(self._fromuser, self._frompassword)
- except Exception:
- log.logger.exception('connect [%s] server failed or username and password incorrect!' % smtp)
- else:
- log.logger.info('email server [%s] login success!' % smtp)
- try:
- smtp.sendmail(self._sender, self._receiver, msg.as_string())
- except Exception:
- log.logger.exception('send email failed!')
- else:
- log.logger.info('send email successed!')
-
-
- # 从文件中读取邮件接收人信息
- def getReceiverInfo(fileName):
- '''
- :param filename: 读取接收邮件人信息
- :return: 接收邮件人信息
- '''
- try:
- openFile = open(os.path.join(mail_path, fileName))
- except Exception:
- log.logger.exception('open or read file [%s] failed,No such file or directory: %s' % (fileName, mail_path))
- else:
- log.logger.info('open file [%s] successed!' % fileName)
- for line in openFile:
- msg = [i.strip() for i in line.split(',')]
- log.logger.info('reading [%s] and got receiver value is [%s]' % (fileName, msg))
- return msg
-
-
- if __name__ == '__main__':# 运营此文件来验证写的send_email是否正确
- readMsg = getReceiverInfo('mail_receiver.txt')
- sendmail = send_email(readMsg)
- sendmail.sendEmail('report.html')
| 1 |
|
- import os
- import logging
- import time
- from logging.handlers import TimedRotatingFileHandler
- import getpathInfo
-
- path = getpathInfo.get_Path()
- log_path = os.path.join(path, 'logs') # 存放log文件的路径
-
- class Logger(object):
- def __init__(self, logger_name='logs…'):
- self.logger = logging.getLogger(logger_name)
- logging.root.setLevel(logging.NOTSET)
- currTime = time.strftime("%Y-%m-%d")
- self.log_file_name = currTime+'logs' # 日志文件的名称
- self.backup_count = 5 # 最多存放日志的数量
- # 日志输出级别
- self.console_output_level = 'WARNING'
- self.file_output_level = 'DEBUG'
- # 日志输出格式
- self.formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
-
- def get_logger(self):
- """在logger中添加日志句柄并返回,如果logger已有句柄,则直接返回"""
- if not self.logger.handlers: # 避免重复日志
- console_handler = logging.StreamHandler()
- console_handler.setFormatter(self.formatter)
- console_handler.setLevel(self.console_output_level)
- self.logger.addHandler(console_handler)
-
- # 每天重新创建一个日志文件,最多保留backup_count份
- file_handler = TimedRotatingFileHandler(filename=os.path.join(log_path, self.log_file_name), when='D',
- interval=1, backupCount=self.backup_count, delay=True,
- encoding='utf-8')
- file_handler.setFormatter(self.formatter)
- file_handler.setLevel(self.file_output_level)
- self.logger.addHandler(file_handler)
- return self.logger
-
-
- logger = Logger().get_logger()
mail_receiver.txt #存放接受人邮箱地址 read_json.py # 读取单一测试数据
-
- import json
- import getpathInfo
-
- import os
-
-
- class ReadJson(object):
- def __init__(self,filename):
- path = getpathInfo.get_Path()
- self.filepath = os.path.join(path, 'data')+"/"+filename
-
- def read_json(self):
- with open(self.filepath, "r", encoding="utf-8")as f:
- # 调用load方法加载文件流
- return json.load(f)
-
- if __name__ == '__main__':
- data = ReadJson("updateuserpwd.json").read_json()
- arrs = []
- arrs.append((data.get("url"),
- data.get("userId"),
- data.get("data"),
- data.get("success"),
- data.get("message")))
- print(arrs)
read_more_json.py # 读取more测试数据
-
- import json
-
-
- class ReadJson(object):
- def __init__(self,filename):
- self.filepath = '../data/' +filename
-
- def read_json(self):
- with open(self.filepath, "r", encoding="utf-8")as f:
- # 调用load方法加载文件流
- return json.load(f)
-
- if __name__ == '__main__':
- datas = ReadJson("login_more.json").read_json()
- arrs = []
- for data in datas.values():
- arrs.append((data.get("url"),
- data.get("mobile"),
- data.get("code"),
- data.get("expect_result"),
- data.get("status_code")))
- print(arrs)
#测试用例
-
- import requests
-
- class ApiLogin(object):
-
- def api_post_login(self,data):
- #headers定义
- headers = {"Content-Type": "application/x-www-form-urlencoded"}
- #url定义
- url = "http://*******"
- #调用post返回相应对象
- return requests.post(url, headers=headers,data = data)
- #客户经理登录接口
- import sys
- import unittest
- from api.api_login import ApiLogin
- from parameterized import parameterized
- from tools.read_more_json import ReadJson
- import tools.Log
-
- log = tools.Log.logger
-
- #读取数据函数
- def get_data():
- datas = ReadJson('login.json').read_json()
- arrs = []
- for data in datas.values():
- arrs.append((data.get("data"),
- data.get("message"),
- data.get("desc")))
- return arrs
-
- class TestLogin(unittest.TestCase):
- '''登录接口 '''
- @parameterized.expand(get_data()) # 参数化测试用例
- def test_login(self,data,message,desc):
- #调用登录方法
- s = ApiLogin().api_post_login(data)
- #调试使用添加描述
- self._testMethodDoc = desc
- #print('查看响应结果:',s.text)
- #断言响应信息
- self.assertIn(message,s.text)
- # 生成响应日志
- log.info('[%s]响应数据为:[%s]' % (sys._getframe().f_code.co_name, s.text))
-
- if __name__ == '__main__':
- unittest.main()
#测试数据
- {
- "login_001":{
- "data":{"loginName":"admin", "password": "123456","memberPass": "on"},
- "message": "名片管理系统",
- "desc": "正常登录"},
- "login_002": {
- "data":{"loginName":"admin", "password": "123456n","memberPass": "on"},
- "message": "密码不正确",
- "desc": "账号错误登录"},
- "login_003": {
- "data":{"loginName":"admir", "password": "123456","memberPass": "on"},
- "message": "账号不存在",
- "desc": "密码错误登录"},
- "login_004": {
- "data":{"loginName":" ", "password": "123456","memberPass": "on"},
- "message": "帐号密码登录",
- "desc": "密码为空登录"},
- "login_005": {
- "data":{"loginName":"admin", "password": " ","memberPass": "on"},
- "message": "密码不正确",
- "desc":"账号为空登录"},
- "login_006": {
- "data":{"loginName":"adm in", "password": "123456","memberPass": "on"},
- "message": "账号不存在",
- "desc":"账号存在空格登录"},
- "login_007": {
- "data":{"loginName":"admin", "password": "123 456","memberPass": "on"},
- "message": "密码不正确",
- "desc":"密码存在空格登录"},
- "login_008": {
- "data":{"loginName":"admin.", "password": "123456","memberPass": "on"},
- "message": "账号不存在",
- "desc":"账号存在特殊符号登录"},
- "login_009": {
- "data":{"loginName":"admin", "password": "123456.","memberPass": "on"},
- "message": "密码不正确",
- "desc":"密码存在特殊符号登录"},
- "login_010": {
- "data":{"loginName":"admi", "password": "123456","memberPass": "on"},
- "message": "账号不存在",
- "desc":"账号不完整登录"},
- "login_010": {
- "data":{"loginName":"admin", "password": "12345","memberPass": "on"},
- "message": "密码不正确",
- "desc":"密码不完整登录"}
- }
#测试报告

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

