本次主要尝试搭建接口自动化框架,基于 unittest+HTMLTestRunner

框架主要模块:

  config: 存放配置文件

  lib: 封装了一些接口前置函数:处理各种事物

  log: 存放生成的日志文件

  report: 放置生成的html测试报告

  suite: 套件运行器

  testcase: 存放测试用例

  util: 封装了一些公共函数(例如封装了日志模块,操作mysql函数,tool工具类等)

剩下的就看代码吧:

 1 import configparser
2 import os
3 from hashlib import md5
4 from APITestUnittest.util.client.httpclient import HttpClient
5
6
7 class ShowAPI(object):
8 """发送showapi平台的接口"""
9 API_URL = "https://route.showapi.com"
10
11 def __init__(self, showapi_appid=None, secret_key=None):
12 """
13 args:
14 :param showapi_appid: 服务ID
15 :param secret_key: 服务密钥
16 如果实例化没有传入参数,就从配置文件读取
17 """
18 config = configparser.ConfigParser()
19 config_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "config")
20 config.read(config_dir+"/"+"env.ini", encoding="utf-8")
21 if showapi_appid is None:
22 self.showapi_appid = config.get("showapi", "SHOWAPI_APPID")
23 else:
24 self.showapi_appid = showapi_appid
25 if secret_key is None:
26 self.secret_key = config.get("showapi", "SECRET_ID")
27 else:
28 self.secret_key = secret_key
29
30 def gen_signature(self, params=None):
31 """
32 生成签名信息
33 :param params: 请求参数
34 :return: 参数签名的md5值
35 """
36 buff = ""
37 for k in sorted(params.keys()):
38 buff += str(k) + str(params[k])
39 buff += self.secret_key
40 # print(buff)
41 return md5(buff.encode("utf-8")).hexdigest()
42
43 def send(self, path, method, params):
44 """
45 :param path: 接口的路径
46 :param method: 接口的方法
47 :param params: 接口传的参数
48 :return: json
49 """
50 params["showapi_appid"] = self.showapi_appid
51 params["showapi_sign"] = self.gen_signature(params)
52 try:
53 httpclient = HttpClient()
54 url = self.API_URL+'/'+path
55 r = httpclient.send_request(method=method, url=url, params_type="form", data=params)
56 httpclient.close_session()
57 return r
58 except Exception as e:
59 print("调用showapi接口失败",str(e))
 1 '''
2 对requests接口的二次封装
3 目的:
4 1,统一接口调用的方法,为了后续的数据驱动的实现
5 2,让测试用例更加整洁,更加干净
6 '''
7 import requests
8 import json
9 from APITestUnittest.util import LogHandler
10
11 class HttpClient(object):
12 log = LogHandler.LogHandler().setLog()
13 """
14 eg: httpclient = HttpClient()
15 response = httpclient(method, url, data)
16 response = httpclient.send_request(method, url, data)
17 """
18
19 def __init__(self):
20 self.session = requests.session()
21
22 def send_request(self, method, url, params_type="form", data=None, **kwargs):
23 self.log.info("正在进行{0}请求,请求地址:{1},请求参数:{2}".format(method,url,data))
24 method = method.upper()
25 params_type = params_type.upper()
26 # 如果data是字符串,就将其转换成字典
27 if isinstance(data, str):
28 data = json.loads(data)
29 if "GET" == method:
30 response = self.session.request(method=method, url=url, params=data, **kwargs)
31 elif "POST" == method:
32 if 'FORM' == params_type: # 发送表单数据,使用data参数传递
33 response = self.session.request(method=method, url=url, data=data, **kwargs)
34 else: # "JSON" == params_type:发送json数据,使用json从参数传递
35 response = self.session.request(method=method, url=url, json=data, **kwargs)
36 elif "PUT" == method:
37 if 'FORM' == params_type: # 发送表单数据,使用data参数传递
38 response = self.session.request(method=method, url=url, data=data, **kwargs)
39 else: # "JSON" == params_type:发送json数据,使用json从参数传递
40 response = self.session.request(method=method, url=url, json=data, **kwargs)
41 elif "DELETE" == method:
42 if 'FORM' == params_type: # 发送表单数据,使用data参数传递
43 response = self.session.request(method=method, url=url, data=data, **kwargs)
44 else: # "JSON" == params_type:发送json数据,使用json从参数传递
45 response = self.session.request(method=method, url=url, json=data, **kwargs)
46 else:
47 raise ValueError('request method "{}" error'.format(method))
48 return response
49
50 def __call__(self, method, url, params_type="form", data=None, **kwargs):
51 return self.send_request(method, url, params_type, data, **kwargs)
52
53 def close_session(self):
54 self.session.close()
 1 '''
2 连接数据库: 封装数据库的操作函数
3 '''
4 import pymysql
5 from APITestUnittest.util.LogHandler import LogHandler
6
7 class Connect_Mysql(object):
8 conn = None
9 log = LogHandler().setLog()
10
11 def __init__(self, host, username, password, db, charset="utf8", port=3306):
12 self.host = host
13 self.username = username
14 self.password = password
15 self.charset = charset
16 self.db = db
17 self.port = port
18
19 # 连接数据库
20 def connect(self):
21 try:
22 self.conn = pymysql.connect(host=self.host,
23 port=self.port,
24 user=self.username,
25 password=self.password,
26 charset=self.charset,
27 db=self.db)
28 # 创建游标
29 self.cursor = self.conn.cursor()
30 except Exception as e:
31 return e
32
33 # 关闭数据库连接
34 def close(self):
35 self.cursor.close()
36 self.conn.close()
37
38 # 查询一条数据
39 def get_one(self, sql, parmas=()):
40 ret = None
41 try:
42 self.connect()
43 self.cursor.execute(sql, parmas)
44 ret = self.cursor.fetchone()
45 #查询结果为空
46 if ret is ():
47 return None
48 self.close()
49 except Exception as e:
50 print(e)
51 return ret
52
53 # 查询所有记录
54 def get_all(self, sql, parmas=()):
55 result = None
56 try:
57 self.connect()
58 self.cursor.execute(sql, parmas)
59 result = self.cursor.fetchall()
60 if result is ():
61 return None
62 self.close()
63 except Exception as e:
64 print(e)
65 return result
66
67 def __edit(self, sql, parmas):
68 count = 0
69 try:
70 self.connect()
71 count = self.cursor.execute(sql, parmas)
72 self.conn.commit()
73 self.close()
74 except Exception as e:
75 print(e)
76 return count
77
78 # 插入
79 def insert(self, sql, parmas=()):
80 self.log.info(f"{sql}插入成功")
81 return self.__edit(sql,parmas)
82
83 # 修改
84 def update(self, sql, parmas=()):
85 self.log.info(f"{sql}修改成功")
86 return self.__edit(sql, parmas)
87
88 # 删除
89 def delete(self, sql, parmas=()):
90 self.log.info(f"删除语句{sql}删除成功")
91 return self.__edit(sql, parmas)
 1 '''
2 封装了日志类
3
4 '''
5 import logging
6 from APITestUnittest.suites.RunCasesSuite import SuitRunner
7
8 class LogHandler():
9 __log_name = SuitRunner.logname
10 # 创建一个logging对象,收集日志
11 logger = logging.getLogger(__name__)
12 # 设置日志的等级
13 logger.setLevel(level=logging.INFO)
14 """
15 日志,输出到文件,输出到控制台
16 """
17
18 def setLog(self):
19 if not self.logger.handlers:
20 # 日志存放路径
21 filenamePath = f"../log/{self.__log_name}.log"
22 # 设置文件处理器
23 __fhandler = logging.FileHandler(filename=filenamePath, encoding='utf-8')
24 # 设置控制台处理器
25 __shandler = logging.StreamHandler()
26 # 设置格式化
27 # __format = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
28 __format = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
29 # 设置文件处理格式化
30 __fhandler.setFormatter(__format)
31 # 设置控制台处理格式化
32 __shandler.setFormatter(__format)
33 # 添加处理器
34 self.logger.addHandler(__fhandler)
35 self.logger.addHandler(__shandler)
36 return self.logger
 1 '''
2 运行测试case套件,运行测试用例
3 封装套件运行器:
4 '''
5
6 import configparser
7 import os
8 import time
9 import unittest
10 from HTMLTestRunner import HTMLTestRunner
11
12
13 class SuitRunner(object):
14 # 时间戳中不能有冒号
15 __t = time.strftime("%Y-%m-%d %H-%M-%S", time.localtime()).split("-")
16 __t2 = __t[2].split(" ")
17 __s = f"{__t[0]}年{__t[1]}月{__t2[0]}日{__t2[1]}时{__t[3]}分{__t[4]}秒"
18 logname = f"{__t[0]}年{__t[1]}月{__t2[0]}日"
19 __reportname = f"{__t[0]}年{__t[1]}月{__t2[0]}日"
20 runner = None
21
22 def __init__(self):
23 self.config = configparser.ConfigParser()
24 config_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "config")
25 self.config.read(config_dir+'/'+'env.ini', encoding='utf-8')
26 self.report_name = self.__reportname+'.'+'html'
27
28
29 def get_report(self, case_dir = "../testcase/wuye/", pattern = "Test*.py", **kwargs):
30 """
31 基于套件,运行,执行case生成测试报告
32 :param case_dir: case文件所在路径
33 :param pattern: case文件(匹配文件:)
34 :return: None
35 """
36 discover = unittest.defaultTestLoader.discover(start_dir=case_dir, pattern=pattern)
37 # 测试报告配置:
38 report_dir = self.config.get("report", "report_dir")
39 name = self.config.get("report", "project_name")
40 filename = report_dir+self.report_name
41 title = f"{name}接口自动化测试报告"
42
43 description = self.config.get("report", "description")
44 if not os.path.exists(report_dir):
45 os.mkdir(report_dir)
46 with open(filename, "wb") as file:
47 runner = HTMLTestRunner(stream=file, title=title, description=description)
48 runner.run(discover)
 1 '''
2 公司接口:涉及新增,修改,删除公司接口
3 '''
4 import os
5 import unittest
6 import configparser
7 import warnings
8 import jsonpath
9 from APITestUnittest.testcase.wuye.PcGetToken import GetToken
10 from APITestUnittest.util.ConnectMysql import Connect_Mysql
11 from APITestUnittest.util.client.httpclient import HttpClient
12
13 class Company(unittest.TestCase):
14 config = configparser.ConfigParser()
15 PATH = os.path.dirname(os.path.dirname(__file__))
16 config_dir = os.path.join(os.path.dirname(PATH), 'config')
17 config.read(config_dir+"/"+"env.ini", encoding="utf-8")
18 URL = config.get("wuye", "host")
19 db = Connect_Mysql(host='******', username='*****', password='******', db='******')
20
21 @classmethod
22 def setUpClass(cls):
23 warnings.simplefilter('ignore', ResourceWarning)
24 cls.api = HttpClient()
25 cls.header = {
26 "Authorization": "Bearer" + " " + GetToken().get_token()
27 }
28 @classmethod
29 def tearDownClass(cls) -> None:
30 # 删除所有测试数据
31 cls.db.delete("delete from sys_company where name like '测试使用%' and is_delete='0'")
32
33 # 新增公司接口
34 def test_company_01(self, path="********"):
35 url = self.URL+ path
36 parmas = {"name":"测试使用公司","abbreviation":"简称","alias":"test","code":"16","parentId":"2","type":"COMPANY"}
37 ret = self.api.send_request(method='post', url=url, headers=self.header, params_type='json', data=parmas)
38 self.assertEqual(ret.json()['code'], "0")
39 self.assertEqual(ret.json()['data'], True)
40
41 # 新增公司名称字段name重复的公司
42 def test_company_02(self, path="********"):
43 url = self.URL+ path
44 parmas = {"name":"测试使用公司","abbreviation":"简称","alias":"test","code":"17","parentId":"2","type":"COMPANY"}
45 ret = self.api.send_request(method='post', url=url, headers=self.header, params_type='json', data=parmas)
46 print(ret.text)
47 result = self.db.get_all("select * from sys_company where name='测试使用公司' and alias='简称' and is_delete='1'")
48 self.assertIsNone(result)
49 self.assertEqual(ret.json()['code'], "-1")
50 self.assertEqual(jsonpath.jsonpath(ret.json(), "$..message")[0], "该名字已存在")
51
52 # 简称重复的公司
53 def test_company_03(self, path="*******"):
54 url = self.URL + path
55 parmas = {"name": "测试使用公司1简称", "abbreviation": "简称", "alias": "test", "code": "18", "parentId": "2",
56 "type": "COMPANY"}
57 ret = self.api.send_request(method='post', url=url, headers=self.header, params_type='json', data=parmas)
58 print(ret.text)
59 # 验证数据库未新增简称重复的公司
60 result = self.db.get_all("select * from sys_company where name='测试使用公司1' and alias='简称'")
61 self.assertIsNone(result)
62 self.assertEqual(ret.json()['code'], "-1")
63 self.assertEqual(jsonpath.jsonpath(ret.json(), "$..message")[0], "该简称已存在")
64
65 # code重复:路径用***代替
66 def test_company_04(self, path="*****"):
67 url = self.URL + path
68 parmas = {"name": "测试使用公司2", "abbreviation": "简称1", "alias": "test", "code": "16", "parentId": "2",
69 "type": "COMPANY"}
70 ret = self.api.send_request(method='post', url=url, headers=self.header, params_type='json', data=parmas)
71 print(ret.text)
72 # 验证数据库未新增code重复的公司
73 result = self.db.get_all("select * from sys_company where name='测试使用公司2' and code='16'")
74 self.assertIsNone(result)
75 self.assertEqual(ret.json()['code'], "-1")
76 self.assertEqual(jsonpath.jsonpath(ret.json(), "$..message")[0], "该code已存在")
77
78 # 删除公司接口:路径用***代替
79 def test_company_05(self, path="*********"):
80 url = self.URL+ path
81 company_id_value = self.db.get_one("select id from sys_company where name='测试使用公司' and is_delete='1'")
82 parmas ={
83 "id": company_id_value
84 }
85 ret = self.api.send_request(method='post', url=url, headers=self.header, params_type='form', data=parmas)
86 self.assertEqual(ret.json()['code'], "0")
87 self.assertEqual(ret.json()['data'], True)
88
89
90 if __name__ == '__main__':
91 unittest.main()

接口自动化框架搭建Unittes+HTMLTestRunner的更多相关文章

  1. Jmeter+ant+Jenkins接口自动化框架搭建

    摘自:https://testerhome.com/topics/13389 一.背景  上一篇讲了Jmeter 接口自动化-脚本数据分离实例,我们知道怎么利用Jmeter去编写接口自动化脚本,但是接 ...

  2. python接口自动化框架搭建

    一.在搭建接口自动化测试框架前,我觉得先需要想明白以下几点: ① 目前情况下,绝大部分接口协议是http,所以需要对http协议有个基本的了解,如:http协议请求.响应由哪些部分组成,常用的meth ...

  3. python pytest接口自动化框架搭建(一)

    1.首先安装pytest pip install pytest 2.编写单测用例 在pytest框架中,有如下约束: 所有的单测文件名都需要满足test_*.py格式或*_test.py格式. 在单测 ...

  4. Jenkins+ant+Jmeter接口自动化框架搭建

    工具准备 JDK: jdk1.8.0_111 Ant: apache-ant-1.9.9 Jmeter: apache-jmeter-3.1 Jenkins: jenkins-2.7.4 JDK安装 ...

  5. python+request接口自动化框架

    python+request接口自动化框架搭建 1.数据准备2.用python获取Excel文件中测试用例数据3.通过requests测试接口4.根据接口返回的code值和Excel对比 但本章只讲整 ...

  6. 转载:python + requests实现的接口自动化框架详细教程

    转自https://my.oschina.net/u/3041656/blog/820023 摘要: python + requests实现的接口自动化框架详细教程 前段时间由于公司测试方向的转型,由 ...

  7. python + requests实现的接口自动化框架详细教程

    前段时间由于公司测试方向的转型,由原来的web页面功能测试转变成接口测试,之前大多都是手工进行,利用postman和jmeter进行的接口测试,后来,组内有人讲原先web自动化的测试框架移驾成接口的自 ...

  8. UI自动化框架搭建之Python3

    UI自动化框架搭建--unittest 使用的代码是Python3版本,与时俱进哈哈 解释一下我的框架目录接口(每个人框架的目录接口不一样,根据实际要求) common目录:公共模块,这个地方可以存放 ...

  9. 基于Python3.7 Robot Framework自动化框架搭建

    一.Robot Framework 和 Selenium 的区别(面试常问) 主流自动化测试框架有Robot Framework 和 Selenium,请根据实际场景选用不同的框架,以下总结各自优缺点 ...

随机推荐

  1. HC-05底层驱动

    INT8U BT_INIT(const char * pNAME, INT32U BAUD, INT8U ROLE, INT32U PSWD) { INT8U OS_ERR = OS_ERR_NONE ...

  2. 【Java】Files.readAllBytes(Path) 遇见的坑

    Files.readAllBytes(Path)方法把整个文件读入内存,此方法返回一个字节数组,还可以把结果传递给String的构造器,以便创建字符串输出. 在针对大文件的读取的时候,可能会出现内存不 ...

  3. SpringMVC MVC 架构模式

    MVC 架构模式 MVC(Model.View 和 Controller)是一种架构模式,它是按照职责划分的思想将系统操作分为三个模块进行处理,每个模块负责一个职责,而且模块之间可以相互交互通信,共同 ...

  4. 9.7 top:实时显示系统中各个进程的资源占用状况

    top命令 用于实时地对系统处理器状态进行监控,它能够实时地显示系统中各个进程的资源占用状况.该命令可以按照CPU的使用.内存的使用和执行时间对系统任务进程进行排序显示,同时top命令还可以通过交互式 ...

  5. C语言volatile

    原文地址: http://www.cnblogs.com/yc_sunniwell/archive/2010/06/24/1764231.html volatile提醒编译器它后面所定义的变量随时都有 ...

  6. GO语言面向对象01---封装属性与创建对象的方法与工厂模式

    package main import "fmt" /* 面向过程编程:调度大量的变量.函数 ---------- 面向对象编程(OOP=Object Oriented Progr ...

  7. CVPR2019论文观察:感知边缘检测的双向级联网络

    CVPR2019论文观察:感知边缘检测的双向级联网络 BDCN:Bi-Directional Cascade Network for Perceptual Edge Detection 摘要 探索多尺 ...

  8. ARM系列处理器和架构

    从一只ARM到另一只ARM! ARM处理器和架构 当前可用的处理器 ARM1 ARM2 ARM3 ARM4和5 ARM6 ARM7 ARM8 强壮有力的ARM ARM9 ARM10 ARM架构 v1  ...

  9. 重新整理 .net core 实践篇—————静态中间件[二十一]

    前言 简单整理一下静态中间件. 正文 我们使用静态文件调用: app.UseStaticFiles(); 那么这个默认会将我们根目录下的wwwroot作为静态目录. 这个就比较值得注意的,可能刚开始学 ...

  10. 无规矩不成方圆,聊一聊 Spring Boot 中 RESTful 接口设计规范

    在设计接口时,有很多因素要考虑,如接口的业务定位,接口的安全性,接口的可扩展性.接口的稳定性.接口的跨域性.接口的协议规则.接口的路径规则.接口单一原则.接口过滤和接口组合等诸多因素,本篇文章将简要分 ...