Python unittest实现接口自动化测试实战(经验分享)
Python+requests+Excel+unittest+HTMLTestRunner
开始之前:
先来梳理一下我们手动进行接口测试的流程是什么样的?
理解接口文档 —> 编写测试用例 —> 准备测试环境 —> 选择测试工具(Postman)—> 执行测试 —> 检查接口返回数据 —> 最后生成测试报告 —> 将测试报告以邮件的方式发送给相关大佬
那么,何为自动化测试呢?
自动化测试是把以人为驱动的测试行为转化为机器执行的一种过程。
现在,我们来看看具体是怎么实现的吧!
(以下是我自己实现的方式,大家可以参考下,有许多不完善的地方,也遇到了许多的问题,希望大神们多多指教!)
一、环境搭建:
1、安装Python3(具体安装方法可以百度)
2、安装需要用到的相关库requests、openpyxl
pip3 install requests(HTTP请求库)
pip3 install openpyxl(操作Excel表格库)
pip3 install configparser(操作.ini配置文件库)
后续有一个安装所需库的package.py文件,执行安装即可
3、安装一个IDE,推荐使用PyCharm(具体安装方法可以百度)
二、结构的划分
- common:存放一些公共的方法
- config: config.ini 配置文件
- readConfig.py 读取配置文件信息
- case_list.txt 控制该条测试用例的执行或不执行
二、配置文件目录
1、配置文件config.ini
就是用来存放基本不变的东西,比如测试环境、发送邮件的信息,所有一成不变东西都可以放在这个文件下
[code][HTTP] protocol = http # 开发环境 # ip = 192.168.0.128 # port = 3002 # 测试环境 ip = 192.168.0.118 port = 3002 # 生成环境 basics = www.xxx.com [EMAIL] is_send = no title = xxx Test Report mail_host = smtp.163.com mail_user = xxxxxx mail_pass = xxxxxx sender = xxxxxx@163.com receive_user = xxxxxx1@qq.com/xxxxxx2@qq.com [EXCEL] module = A api_name = B case_id = C case_name = D url = E premise = F header = G method = H data = I param = J check = K expected = L return = M rely = N
[EXCEL] 是Excel测试用例表格每一列对应的字母常量,用于后面的读取Excel表格数据使用,比如:用例标题case_name = D
2、读取配置文件readConfig.py
该文件是用来读取config.ini文件的,用到configparser第三方库
[code]#!/usr/bin/python3 # coding=utf-8 import configparser import os proDir = os.path.split(os.path.realpath(__file__))[0] configPath = os.path.join(proDir, "config.ini") class ReadConfig: def __init__(self): self.cf = configparser.ConfigParser() self.cf.read(configPath) def get_base_url(self): protocol = self.cf.get("HTTP", "protocol") # 测试环境 ip = self.cf.get("HTTP", "ip") port = self.cf.get("HTTP", "port") base_url = protocol + '://' + ip + ':' + port # 生成环境 # basics = self.cf.get("HTTP", "basics") # base_url = protocol + '://' + basics return base_url def get_email(self, mail_key): email_value = self.cf.get("EMAIL", mail_key) return email_value
三、测试数据设计
1、Excel测试用例,所有的测试用例都写在Excel表格里,然后读取Excel,进行自动测试
请求头部和请求数据都是写的关键字,关键字对应的数据放在JSON文件
三、公共方法common目录
这里就介绍一下几个主要的方法
1、myLog.py
——主要用到logging模块可以替代print函数的功能,并能将标准输出输入到日志文件保存起来(logging模块是Python标准库,不需要安装)
——我主要对其进行了一下封装定义成自己想要的输出log的格式、控制输出到控制台的等级、需要打印的日志等级,直接看源码吧!
[code]#!/usr/bin/python3 # coding=utf-8 import logging import os import time from datetime import datetime logger = logging.getLogger(__name__) path_dir = str(os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))) logpath = path_dir + '/logs' def create_file(file_name): path = file_name[:-7] if not os.path.isdir(path): os.makedirs(path) if not os.path.isfile(file_name): fb = open(file_name, mode='w', encoding='utf-8') fb.close() else: # print("不需要创建") pass def set_handler(level): if level == 'error': logger.addHandler(MyLog.err_handler) logger.addHandler(MyLog.handler) logger.addHandler(MyLog.console) def remove_handler(level): if level == 'error': logger.removeHandler(MyLog.err_handler) logger.removeHandler(MyLog.handler) logger.removeHandler(MyLog.console) def set_now_time(): now_time = time.strftime(MyLog.time_format, time.localtime(time.time())) return now_time class MyLog: # 基本设置 path = os.path.join(logpath, str(datetime.now().strftime("%Y%m%d"))) now = time.strftime("%H-%M-%S", time.localtime(time.time())) # 获取当前时间 log_file = path + "/log.log" err_file = path + "/err.log" create_file(log_file) create_file(err_file) time_format = "%Y-%m-%d %H:%M:%S" # 设置需要输出到控制台的等级 console = logging.StreamHandler() # console.setLevel(logging.WARNING) console.setLevel(logging.INFO) # 设置需要打印的日志等级 logger.setLevel(logging.DEBUG) handler = logging.FileHandler(log_file, encoding='utf-8') err_handler = logging.FileHandler(err_file, encoding='utf-8') @staticmethod def debug(message, name='', line=''): set_handler('debug') if name != '' and line != '': logger.debug( "[" + set_now_time() + "]" + " - " + name + ', ' + 'Line %s' % line + "] " + " - " + "DEBUG:" + " " + message) remove_handler('debug') else: logger.debug( "[" + set_now_time() + "]" + " - " + "DEBUG:" + " " + message) remove_handler('debug') @staticmethod def info(message, name='', line=''): set_handler('info') if name != '' and line != '': logger.info( "[" + set_now_time() + "]" + " - " + name + ', ' + 'Line %s' % line + "] " + " - " + "INFO:" + " " + message) remove_handler('info') else: logger.info( "[" + set_now_time() + "]" + " - " + "INFO:" + " " + message) remove_handler('info') @staticmethod def warning(message, name='', line=''): set_handler('warning') if name != '' and line != '': logger.warning( "[" + set_now_time() + "]" + " - " + name + ', ' + 'Line %s' % line + "] " + " - " + "WARNING:" + " " + message) remove_handler('warning') else: logger.warning( "[" + set_now_time() + "]" + " - " + "WARNING:" + " " + message) remove_handler('warning') @staticmethod def error(message, name='', line=''): set_handler('error') if name != '' and line != '': logger.error( "[" + set_now_time() + "]" + " - " + name + ', ' + 'Line %s' % line + "] " + " - " + "ERROR:" + " " + message) remove_handler('error') else: logger.error( "[" + set_now_time() + "]" + " - " + "ERROR:" + " " + message) remove_handler('error')
2、httpSet.yp
——requests是python http 库,它是第三方库,所以使用前需要先安装(具体使用可以查阅官方文档http://cn.python-requests.org/zh_CN/latest/)
——主要是对requests的一些请求方式进行了一下封装修改,就不多介绍了,直接看源码吧!
[code]#!/usr/bin/python3 # coding=utf-8 import json import requests from common.myLog import MyLog false = False true = True class HttpMethod: def __init__(self): self.log = MyLog() def get_method(self, url, data=None, headers=None): try: res = requests.get(url=url, params=data, headers=headers) status_code = res.status_code res_json = res.json() return status_code, res_json # 返回响应码,响应内容 except Exception as e: self.log.error("Error:%s" % e) def post_method(self, url, files=None, data=None, headers=None): try: if files: res = requests.post(url=url, files=files, data=data, headers=headers) else: res = requests.post(url=url, data=json.dumps(data), headers=headers) status_code = res.status_code res_json = res.json() return status_code, res_json # 返回响应码,响应内容 except Exception as e: self.log.error("Error:%s" % e) def put_method(self, url, data=None, headers=None): try: res = requests.put(url=url, data=json.dumps(data), headers=headers) status_code = res.status_code res_json = res.json() return status_code, res_json # 返回响应码,响应内容 except Exception as e: self.log.error("Error:%s" % e) def delete_method(self, url, data=None, headers=None): try: res = requests.delete(url=url, data=json.dumps(data), headers=headers) status_code = res.status_code res_json = res.json() return status_code, res_json # 返回响应码,响应内容 except Exception as e: self.log.error("Error:%s" % e) def http_method(self, method, url, files=None, data=None, headers=None): """判断请求方法 :param method: 请求方法 :param url: 接口路径 :param data: 请求数据 :param headers: 请求头 :return: """ if method == 'get': status_code, res_json = self.get_method(url, data, headers) elif method == 'post': status_code, res_json = self.post_method(url, files, data, headers) elif method == 'put': status_code, res_json = self.put_method(url, data, headers) else: status_code, res_json = self.delete_method(url, data, headers) return status_code, json.dumps(res_json, ensure_ascii=False, sort_keys=False, indent=2) # 对json数据进行格式化输出
3、operationExcelSheet.py
——openpyxl是python第三方库,使用前需要先安装(具体使用可以百度一下)
——主要是对Excel表格里面的不同sheet进行读写操作,就不啰嗦啦,看源码吧!
[code]#!/usr/bin/python3 # coding=utf-8 # Excel有多个sheet import os from openpyxl import load_workbook proDir = os.path.split(os.path.realpath(__file__))[0] excelPath = os.path.join(proDir, "../testDataFile/TestCase.xlsx") class OperationExcel: def __init__(self): self.open_excel = load_workbook(excelPath) # 打开Excel表格 def open_excel_sheet(self, sheet_name): """设置需要操作的sheet :param sheet_name: 表名 :return: """ return self.open_excel[sheet_name] def from_ab_get_data(self, sheet_name, cell, row): """通过单元格获取数据,例如:A2 :param sheet_name: 表名 :param cell: 所在列A, B, ... :param row: 所在行1, 2, ... :return: 对应单元格的值 """ open_sheet = self.open_excel_sheet(sheet_name) value = open_sheet[cell + str(row)].value return value def from_xy_get_data(self, sheet_name, x, y): """ 通过单元格坐标获取数据,例如:(1, 2) :param sheet_name: 表名 :param x: 横坐标x :param y: 纵坐标y :return:返回该坐标(x, y)对应的数据 """ open_sheet = self.open_excel_sheet(sheet_name) value = open_sheet.cell(x, y).value return value def write_data(self, sheet_name, cell, row, write_value): """写入数据 :param sheet_name: 表名 :param cell: 所在列A, B, ... :param row: 所在行1, 2, ... :param write_value: 写入的值 :return: """ wb = load_workbook(filename=excelPath) ws = wb[sheet_name] ws[cell + str(row)] = write_value wb.save(filename=excelPath)
4、operationJson.py
——json是python标准库,不需要安装
——主要是对json数据文件进行读写操作,这里默认对data.json这个文件进行操作,如果要指定操作某个json文件只需要在调用该方法的时候传入json文件路径即可,其它也没啥好说的,看源码吧!
[code]#!/usr/bin/python3 # coding=utf-8 import json import os proDir = os.path.split(os.path.realpath(__file__))[0] jsonPath = os.path.join(proDir, "../testDataFile/data.json") class OperationJson: def __init__(self, file_name=None): if file_name: self.file_name = file_name else: self.file_name = jsonPath def open_json(self): """打开json文件 :return:返回json文件数据 """ with open(self.file_name, 'r') as fp: data = json.load(fp) return data fp.close() def key_get_data(self, key): """通过key值获取数据 :param key: 需要获取的值对应的key :return: """ data = self.open_json()[key] return data def write_data(self, w_data, key1, key2=None): """修改json数据 :param w_data: 修改后的数据 :param key1: 要修改的键值1 :param key2: 要修改的键值2 :return: """ data_dict = self.open_json() if key2 == None: data_dict[key1] = w_data else: data_dict[key1][key2] = w_data with open(self.file_name, 'w') as fp: fp.write(json.dumps(data_dict, ensure_ascii=False, sort_keys=False, indent=2)) # 对写入的json数据进行格式化 fp.close()
5、readTestData.py
——该文件是读取测试数据的,这个比较简单了,就是结合operationExcelSheet.py和operationJson.py一起使用,看具体源码吧!
——因为我在执行测试的时候有遇到python不能识别true和false,所以我在开头定义全局变量false = False 和 true = True
[code]#!/usr/bin/python3 # coding=utf-8 from common.operationExcelSheet import OperationExcel from common.operationJson import OperationJson from config.readConfig import ReadConfig false = False true = True class ReadTestData: def __init__(self, file_name=None): self.open_excel = OperationExcel() self.set_excel = ReadConfig() if file_name: self.open_json = OperationJson(file_name) else: self.open_json = OperationJson() def get_module(self, sheet_name, row): cell = self.set_excel.get_excel('module') module = self.open_excel.from_ab_get_data(sheet_name, cell, row) return module def get_api_name(self, sheet_name, row): cell = self.set_excel.get_excel('api_name') case_id = self.open_excel.from_ab_get_data(sheet_name, cell, row) return case_id def get_case_id(self, sheet_name, row): cell = self.set_excel.get_excel('case_id') case_id = self.open_excel.from_ab_get_data(sheet_name, cell, row) return case_id def get_case_name(self, sheet_name, row): cell = self.set_excel.get_excel('case_name') case_title = self.open_excel.from_ab_get_data(sheet_name, cell, row) return case_title def get_url(self, sheet_name, row): cell = self.set_excel.get_excel('url') url = self.open_excel.from_ab_get_data(sheet_name, cell, row) return url def get_premise(self, sheet_name, row): cell = self.set_excel.get_excel('premise') premise = self.open_excel.from_ab_get_data(sheet_name, cell, row) return premise def get_header(self, sheet_name, row): cell = self.set_excel.get_excel('header') headers_key = self.open_excel.from_ab_get_data(sheet_name, cell, row) headers = self.open_json.key_get_data(headers_key) return headers def get_method(self, sheet_name, row): cell = self.set_excel.get_excel('method') method = self.open_excel.from_ab_get_data(sheet_name, cell, row) return method def get_request_data(self, sheet_name, row): cell = self.set_excel.get_excel('data') request_key = self.open_excel.from_ab_get_data(sheet_name, cell, row) # request_list = request_str.split(',') # print(request_list) request_data = self.open_json.key_get_data(request_key) return request_data def get_param(self, sheet_name, row): cell = self.set_excel.get_excel('param') param = self.open_excel.from_ab_get_data(sheet_name, cell, row) request_param = self.open_json.key_get_data(param) return request_param def get_check(self, sheet_name, row): cell = self.set_excel.get_excel('check') check = self.open_excel.from_ab_get_data(sheet_name, cell, row) return check def get_expect_result(self, sheet_name, row): cell = self.set_excel.get_excel('expected') expect_result = self.open_excel.from_ab_get_data(sheet_name, cell, row) expect_result_dict = eval(expect_result) return expect_result_dict def get_return_data(self, sheet_name, row): cell = self.set_excel.get_excel('return') return_data = self.open_excel.from_ab_get_data(sheet_name, cell, row) return return_data def get_rely_data(self, sheet_name, row): cell = self.set_excel.get_excel('rely') data = self.open_excel.from_ab_get_data(sheet_name, cell, row) rely_data = self.open_json.key_get_data(data) return rely_data
6、sendEmail.py
——该文件是构造发送邮件的方法,用到python标准库smtplib和email
——可以获取最新的测试报告,把最新的测试报告以文本和附件的形式发送,看源码吧~
[code]#!/usr/bin/python3 # coding=utf-8 import smtplib import os from common.myLog import MyLog from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.header import Header from config.readConfig import ReadConfig # 路径 path_dir = str(os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))) reportpath = path_dir + '/report' local_readConfig = ReadConfig() class SendEmail: def __init__(self): global host, user, password, sender, title host = local_readConfig.get_email('mail_host') # 邮箱服务器 user = local_readConfig.get_email('mail_user') # 发件人用户名 password = local_readConfig.get_email('mail_pass') # 发件人邮箱授权码,非登录密码 sender = local_readConfig.get_email('sender') # 发件人邮箱 title = local_readConfig.get_email('title') # 邮件标题 self.logger = MyLog() self.receive_user = local_readConfig.get_email('receive_user') # 收件人邮箱 self.receive_user_list = [] for i in str(self.receive_user).split('/'): self.receive_user_list.append(i) def send_email(self): """把最新的测试报告以邮件的方式发送""" # 构造邮件 file_new = self.get_new_report() f = open(file_new, 'rb') content = f.read() message = MIMEMultipart() message['From'] = "{}".format(sender) # 发件人 message['To'] = ",".join(self.receive_user_list) # 收件人 message['Subject'] = Header(title, 'utf-8') # 标题 message.attach(MIMEText(content, 'html', 'utf-8')) # 添加附件 filename = file_new[-31:] att = MIMEText(content, 'base64', 'utf-8') att["Content-Type"] = 'application/octet-stream' att["Content-Disposition"] = 'attachment; filename=%s' % filename message.attach(att) # 发送邮件 try: server = smtplib.SMTP() server.connect(host) server.login(user, password) # 登录验证 server.sendmail(sender, self.receive_user_list, message.as_string()) # 发送 server.quit() # 关闭 self.logger.info("邮件发送成功!") except smtplib.SMTPException as e: # print("邮件发送失败!") self.logger.error("邮件发送失败!请检查邮件配置%s" % e) def get_new_report(self): """获取最新的测试报告""" lists = os.listdir(reportpath) if lists: lists.sort(key=lambda fn: os.path.getmtime(reportpath + '\\' + fn)) file_new = os.path.join(reportpath, lists[-1]) return file_new
四、测试用例代码
测试用例代码文件名一定要以test开头命名,用的是unittest框架进行编写的,测试过程分三步:
- 第一步:获取请求数据
- 第二步:发送请求,获取返回结果数据
- 第三步:对结果数据进行断言
最好把每一步的操作都打印到log里面,方便测试结束后查看,嗯...看源码吧!
[code]#!/usr/bin/python3 # coding=utf-8 import json import os import unittest from common.httpSet import HttpMethod from common.myLog import MyLog from common.operationJson import OperationJson from common.readTestData import ReadTestData from config.readConfig import ReadConfig from common.getRunLine import get_run_line proDir = os.path.split(os.path.realpath(__file__))[0] file_name = os.path.join(proDir, "../../testDataFile/orchestrator_account.json") print('file_name:%s' % file_name) class LoginTest(unittest.TestCase): def setUp(self): self.data = ReadTestData(file_name) self.hea_data = ReadTestData() self.http = HttpMethod() self.config = ReadConfig() self.log = MyLog() self.json = OperationJson() self.sheet = 'app_test_case' self.row = list(range(2, 7)) self.log.info(message="----------测试开始----------", name="test01_OrcLogin.py") def tearDown(self): self.log.info(message="----------测试结束----------", name="test01_OrcLogin.py") def test_login01(self): """orc admin正常登录""" self.log.info(message="test_login01", name="test01_OrcLogin.py", line=get_run_line()) # 获取测试数据 method = self.data.get_method(self.sheet, self.row[0]) url = self.config.get_base_url() + self.data.get_url(self.sheet, self.row[0]) headers = self.hea_data.get_header(self.sheet, self.row[0]) data = self.data.get_request_data(self.sheet, self.row[0]) self.log.info(message="第一步: 获取请求数据") self.log.info(message="请求方法:%s" % method) self.log.info(message="请求接口:%s" % url) self.log.info(message="请求数据:%s" % data) # 发送请求 status_code, res_json = self.http.http_method(method=method, url=url, data=data, headers=headers) dict_json = json.loads(res_json) # 把json数据转换成字典对象 # print("dict_json:%s" % dict_json) # print("dict_json type:%s" % type(dict_json)) # print("dict_json type:%s" % type(res_json)) self.log.info(message="第二步:发送请求,获取返回数据:") self.log.info(message="%s" % res_json) if dict_json["status"]: orc_token = dict_json["orchestrator_admin_token"] # 提取orc_token self.log.info(message="提取orc_token", name="test_login01") self.log.info(message="%s" % orc_token, name="test_login01") authorization = "Bearer " + orc_token self.json.write_data(authorization, "orc_token_header", "Authorization") # 把orc_token写入json文件 # 断言 self.log.info(message="第三步:断言") self.assertEqual(status_code, 200, msg=">>>接口请求失败") self.assertTrue(dict_json["status"], msg=">>>断言失败,实际返回结果:%s" % dict_json) self.assertEqual(dict_json["username"], "orc_admin", msg=">>>断言失败,实际返回值是:%s" % dict_json["username"]) def test_login02(self): """登录失败,密码错误""" self.log.info(message="test_login02", name="test01_OrcLogin.py", line=get_run_line()) # 获取测试数据 method = self.data.get_method(self.sheet, self.row[1]) url = self.config.get_base_url() + self.data.get_url(self.sheet, self.row[1]) headers = self.hea_data.get_header(self.sheet, self.row[1]) data = self.data.get_request_data(self.sheet, self.row[1]) expect = self.data.get_expect_result(self.sheet, self.row[1]) self.log.info(message="第一步: 获取请求数据") self.log.info(message="请求方法:%s" % method) self.log.info(message="请求接口:%s" % url) self.log.info(message="请求数据:%s" % data) self.log.info(message="期望结果:%s" % expect) # 发送请求 status_code, res_json = self.http.http_method(method=method, url=url, data=data, headers=headers) dict_json = json.loads(res_json) # 把json数据转换成字典对象 self.log.info(message="第二步:发送请求,获取返回数据:") self.log.info(message="%s" % res_json) self.log.info(message="第三步:断言") # 断言 self.assertEqual(status_code, 200, msg=">>>接口请求失败") self.assertFalse(dict_json["status"], msg=">>>断言失败,实际返回结果:%s" % dict_json) self.assertEqual(dict_json["err"]["code"], expect["err"]["code"], msg=">>>断言失败,实际返回结果:%s" % dict_json["err"]["code"]) self.assertEqual(dict_json["err"]["message"], expect["err"]["message"], msg=">>>断言失败,实际返回结果:%s" % dict_json["err"]["message"]) def test_login03(self): """登录失败,账户不存在""" self.log.info(message="test_login03", name="test01_OrcLogin.py", line=get_run_line()) # 获取测试数据 method = self.data.get_method(self.sheet, self.row[2]) url = self.config.get_base_url() + self.data.get_url(self.sheet, self.row[2]) headers = self.hea_data.get_header(self.sheet, self.row[2]) data = self.data.get_request_data(self.sheet, self.row[2]) expect = self.data.get_expect_result(self.sheet, self.row[2]) self.log.info(message="第一步: 获取请求数据") self.log.info(message="请求方法:%s" % method) self.log.info(message="请求接口:%s" % url) self.log.info(message="请求数据:%s" % data) self.log.info(message="期望结果:%s" % expect) # 发送请求 status_code, res_json = self.http.http_method(method=method, url=url, data=data, headers=headers) dict_json = json.loads(res_json) # 把json数据转换成字典对象 self.log.info(message="第二步:发送请求,获取返回数据:") self.log.info(message="%s" % res_json) self.log.info(message="第三步:断言") # 断言 self.assertEqual(status_code, 200, msg=">>>接口请求失败") self.assertFalse(dict_json["status"], msg=">>>断言失败,实际返回结果:%s" % dict_json) self.assertEqual(dict_json["err"]["code"], expect["err"]["code"], msg=">>>断言失败,实际返回结果:%s" % dict_json["err"]["code"]) self.assertEqual(dict_json["err"]["message"], expect["err"]["message"], msg=">>>断言失败,实际返回结果:%s" % dict_json["err"]["message"]) # @unittest.skip("跳过测试") def test_login04(self): """登录失败,缺少username字段""" self.log.info(message="test_login04", name="test01_OrcLogin.py", line=get_run_line()) # 获取测试数据 method = self.data.get_method(self.sheet, self.row[3]) url = self.config.get_base_url() + self.data.get_url(self.sheet, self.row[3]) headers = self.hea_data.get_header(self.sheet, self.row[3]) data = self.data.get_request_data(self.sheet, self.row[3]) expect = self.data.get_expect_result(self.sheet, self.row[3]) self.log.info(message="第一步: 获取请求数据") self.log.info(message="请求方法:%s" % method) self.log.info(message="请求接口:%s" % url) self.log.info(message="请求数据:%s" % data) self.log.info(message="期望结果:%s" % expect) # 发送请求 status_code, res_json = self.http.http_method(method=method, url=url, data=data, headers=headers) dict_json = json.loads(res_json) # 把json数据转换成字典对象 self.log.info(message="第二步:发送请求,获取返回数据:") self.log.info(message="%s" % res_json) self.log.info(message="第三步:断言") # 断言 self.assertEqual(status_code, 200, msg=">>>接口请求失败") self.assertFalse(dict_json["status"], msg=">>>实际返回结果:%s" % dict_json) self.assertEqual(dict_json["err"]["code"], expect["err"]["code"], msg=">>>断言失败,实际返回结果:%s" % dict_json["err"]["code"]) self.assertEqual(dict_json["err"]["message"], expect["err"]["message"], msg=">>>断言失败,实际返回结果:%s" % dict_json["err"]["message"]) def test_login05(self): """登录失败,缺少password字段""" self.log.info(message="test_login05", name="test01_OrcLogin.py", line=get_run_line()) # 获取测试数据 method = self.data.get_method(self.sheet, self.row[4]) url = self.config.get_base_url() + self.data.get_url(self.sheet, self.row[4]) headers = self.hea_data.get_header(self.sheet, self.row[4]) data = self.data.get_request_data(self.sheet, self.row[4]) expect = self.data.get_expect_result(self.sheet, self.row[4]) self.log.info(message="第一步: 获取请求数据") self.log.info(message="请求方法:%s" % method) self.log.info(message="请求接口:%s" % url) self.log.info(message="请求数据:%s" % data) self.log.info(message="期望结果:%s" % expect) # 发送请求 status_code, res_json = self.http.http_method(method=method, url=url, data=data, headers=headers) dict_json = json.loads(res_json) # 把json数据转换成字典对象 self.log.info(message="第二步:发送请求,获取返回数据:") self.log.info(message="%s" % res_json) self.log.info(message="第三步:断言") # 断言 self.assertEqual(status_code, 200, msg=">>>接口请求失败") self.assertFalse(dict_json["status"], msg=">>>断言失败,实际返回结果:%s" % dict_json) self.assertEqual(dict_json["err"]["code"], expect["err"]["code"], msg=">>>断言失败,实际返回结果:%s" % dict_json["err"]["code"]) self.assertEqual(dict_json["err"]["message"], expect["err"]["message"], msg=">>>断言失败,实际返回结果:%s" % dict_json["err"]["message"]) if __name__ == "__main__": unittest.main()
五、执行文件
测试用例代码写完之后,现在就要写一个执行文件,只需要执行该文件就可以跑所有测试用例了,它就是runcase.py文件
1、添加测试套件
把所有要执行的测试用例写到case_list.txt文件,如果是有些用例想暂时不执行,就可以在开头添加"#"
2、执行测试
在使用之前需要下载HTMLTestRunner包(可以官方下载pip install html-testRunner),也可以在网上找一个别的大神做过优化的HTMLTestRunner包下载下来直接用(我也是这么干的~~)
用到测试报告模块HTMLTestRunner来执行测试套件并生成html测试报告,废话就不多说了,看源码吧!
[code]#!/usr/bin/python3 # coding=utf-8 import os import sys import time import unittest from common.HTMLTestRunnerNew import HTMLTestRunner from common.myLog import MyLog from common.sendEmail import SendEmail from config.readConfig import ReadConfig from reset_env import ResetEnv proDir = os.path.split(os.path.realpath(__file__))[0] case_list_path = os.path.join(proDir, "case_list.txt") test_case_path = os.path.join(proDir, "testCase") class RunTest: def __init__(self): self.logger = MyLog() self.readconfig = ReadConfig() self.send_mail = SendEmail() self.env = ResetEnv() self.is_send = self.readconfig.get_email("is_send") # 测试报告基本信息 self.testers = "HaiYi" self.title = "XXX接口测试报告" self.description = "测试环境:Develop,IP地址:%s" % self.readconfig.get_base_url() # 导入TestCase目录下的全部测试用例 # self.discover = unittest.defaultTestLoader.discover(test_case_path, pattern='test*.py') # 导入指定测试用例列表文件 self.case_list_file = case_list_path self.case_list_list = [] # 重置测试环境 self.is_env = self.env.delete_db() def get_case_list(self): """获取需要进行运行的测试用例列表""" fb = open(self.case_list_file) for i in fb.readlines(): data = str(i) if data != '' and not data.startswith('#'): self.case_list_list.append(data.replace('\n', '')) fb.close() print(self.case_list_list) def set_test_suite(self): """设置添加测试套件""" self.get_case_list() test_suite = unittest.TestSuite() suite_module = [] for case in self.case_list_list: case_name = case.split('/')[-1] print(case_name + '.py') discover = unittest.defaultTestLoader.discover(test_case_path, pattern=case_name + '.py') suite_module.append(discover) if len(suite_module) > 0: for suite in suite_module: for test_name in suite: test_suite.addTest(test_name) else: return None return test_suite def run_test(self): """执行测试""" if self.is_env: try: test_suite = self.set_test_suite() # 获取测试套件 now = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime(time.time())) # 获取当前日期时间 public_path = os.path.dirname(os.path.abspath(sys.argv[0])) # filename = public_path + "/report/" + now + "_report.html" # 保存的报告路径和名称 filename = public_path + "/report/" + "index.html" # 保存的报告路径和名称 print("测试报告目录:%s" % filename) fp = open(filename, 'wb') runner = HTMLTestRunner(stream=fp, tester=self.testers, title=self.title, description=self.description ) if test_suite is not None: runner.run(test_suite) # 执行指定添加的测试用例套件 # runner.run(self.discover) # 执行TestCase目录下的全部测试用例 else: self.logger.info("Have no case to test.") except Exception as e: self.logger.error(str(e)) finally: self.logger.warning("---------------All Test End---------------") fp.close() # 发送电子邮件 if self.is_send == 'yes': self.send_mail.send_email() self.logger.warning("测试报告已发送电子邮件!") elif self.is_send == 'no': self.logger.warning("测试报告不发送电子邮件!") else: self.logger.error("测试报告发送电子邮件为未知状态,请检查配置!") else: self.logger.warning("测试环境清理失败的,无法继续执行测试!!!") if __name__ == "__main__": run = RunTest() run.run_test()
到这里就全部介绍完成了,我知道有许多不完善的地方,在开发过程中也遇到了许多的问题,希望大神们多多指教,谢谢!
下面奉上两张测试报告的截图,一个是HTML的,一个邮箱的
HTML文件测试报告:
邮箱测试报告:
- 简单实现接口自动化测试(基于python+unittest)
- 简单实现接口自动化测试(基于python+unittest)
- 笔记-Python2+unittest实现接口自动化测试
- Python+request+unittest实现接口测试框架集成实例
- selenium+python+unittest实现自动化测试(入门篇)
- Python+requests+unittest+excel实现接口自动化测试框架
- python+unittest+requests实现接口自动化的方法
- python接口自动化测试(八)-unittest-生成测试报告
- Python+request+unittest实现接口测试框架集成实例
- python实现接口测试到unittest框架集成
- python 例子2: 利用 unittest 实现自动加载并执行它定义的方法
- Python利用unittest框架编写接口测试小栗子
- python接口自动化(二十五)--unittest断言——下(详解)
- python 接口自动化测试--代码实现(七)
- python接口自动化(二十二)--unittest执行顺序隐藏的坑(详解)
- 【无私分享:ASP.NET CORE 项目实战(第二章)】添加EF上下文对象,添加接口、实现类以及无处不在的依赖注入(DI)
- python unittest 自动化测试
- python unittest实现api自动化测试
- Python+unittest+requests+HTMLTestRunner 完整的接口自动化测试框架搭建_00——框架结构简解
- python接口自动化(二十五)--unittest断言——下(详解)