├── .gitignore ├── .idea ├── .gitignore ├── APItest.iml ├── encodings.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml └── modules.xml ├── 1.py ├── Common ├── 1.py ├── Base_test.py ├── Login.py ├── __init__.py ├── xmltest.py └── yhapi.py ├── Config └── conf.ini ├── Logs └── log.py ├── README.md ├── data ├── a.xls ├── aBAK.xls └── json │ └── 1.py ├── getcwd.py ├── rdfile ├── __init__.py ├── rdExcel.py └── rdini.py ├── report └── allure_result │ ├── 32425007-3e44-4707-824b-5eb16ca4f7b7-result.json │ ├── c6868cd2-d85d-4095-9c18-c0e73e7c531d-container.json │ └── dea0c60d-318d-4f99-a7b0-1d892cb8ca32-attachment.txt └── test_pytest ├── __init__.py ├── test_api.py └── test_two.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/APItest.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2021-4-22 11:31 3 | # @Author : 奥斯卡 4 | # @Email : 1203069758@qq.com 5 | # @File : 1.py 6 | 7 | from Logs.log import log1 8 | try: 9 | log1.info("开始测试") 10 | r = 10/0 11 | log1.info("resuit:", r) 12 | except ZeroDivisionError as e: 13 | log1.error("报错信息:", exc_info=1) 14 | log1.info('end') 15 | 16 | 17 | -------------------------------------------------------------------------------- /Common/1.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | os.path.dirname(__file__) # 获取当前目录 4 | os.path.dirname(os.path.dirname(__file__)) # 获取当前目录的上一级 5 | 6 | 7 | # 获取指定的目录 8 | def fileDir(data): 9 | """ 10 | :param data: 目录 11 | :return: 返回 12 | """ 13 | base_path = os.path.dirname(os.path.dirname(__file__)) 14 | return os.path.join(base_path, data) # 将获取到的目录返回 15 | 16 | 17 | # 获取路径下的文件,调用需要传递两个参数替换,否则使用默认的参数 18 | def filePath(fileDir="data", fileName="data.xls"): 19 | """ 20 | :param fileDir:目录 21 | :param fileName: 文件名称 22 | :return: 返回 23 | """ 24 | return os.path.join(os.path.dirname(os.path.dirname(__file__)), fileDir, fileName) 25 | 26 | print(fileDir('test_api.py')) 27 | -------------------------------------------------------------------------------- /Common/Base_test.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from Logs.log import log1 3 | import json 4 | 5 | class ApiRequest(object): 6 | # -----第一种请求方式封装request库,调用可根据实际情况传参 7 | def send_requests(self, method, url, data=None, params=None, headers=None, cookies=None, json=None, files=None, 8 | timeout=None): 9 | self.res = requests.request(method, url, data=data, params=params, headers=headers, cookies=cookies, json=json, 10 | files=files, timeout=timeout) 11 | return self.res 12 | 13 | 14 | def getdict(self, dict1, obj, default=None): 15 | ''' 遍历嵌套字典,得到想要的value 16 | dict1所需遍历的字典 17 | obj 所需value的键''' 18 | for k, v in dict1.items(): 19 | if k == obj: 20 | return v 21 | else: 22 | if type(v) is dict: # 如果是字典 23 | re = self.getdict(v, obj, default) # 递归 24 | if re is not default: 25 | return re 26 | 27 | def get_header(self, headers): 28 | ''' 29 | 将从浏览器复制过来的headers转换成字典 30 | ''' 31 | headers = dict([line.split(": ", 1) for line in headers.split("\n")]) 32 | return headers 33 | 34 | # c='''Connection: keep-alive 35 | # Content-Length: 110 36 | # Pragma: no-cache 37 | # Cache-Control: no-cache 38 | # Accept: application/json, text/plain, */* 39 | # User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36 40 | # Content-Type: application/json;charset=UTF-8 41 | # Origin: http://172.25.16.6:8090 42 | # Referer: http://172.25.16.6:8090/ 43 | # Accept-Encoding: gzip, deflate 44 | # Accept-Language: zh-CN,zh;q=0.9''' 45 | # headers = dict([line.split(": ", 1) for line in c.split("\n")]) 46 | # print(headers) 47 | -------------------------------------------------------------------------------- /Common/Login.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2021-5-7 19:25 3 | # @Author : 奥斯卡 4 | # @Email : 1203069758@qq.com 5 | # @File : Login.py 6 | import requests 7 | import re 8 | 9 | 10 | class cookie(): 11 | def __init__(self): 12 | '''这里登录用的账号密码是zzzfwld/12345678Aa''' 13 | self.url = "http://localhost:999/bi/api?action=login" 14 | self.headers = {'content-type': 'application/x-www-form-urlencoded'} 15 | self.data = {'adminv': 'admin', 'passv': 'g5'} 16 | self.request = requests.session() 17 | 18 | def login(self): 19 | ''' 20 | :return: 这一步是登录,返回登录成功的cookies.后面的操作拿到cookies,就能直接操作接口了 21 | ''' 22 | # 先获取拼接到的的data和cookies。因为创建session的时候需要cookie 23 | # 使用旧cookie登录,然后得到登录后的新cookie 24 | # 使用新cookie就可以操作接口 25 | res = self.request.post(self.url, headers=self.headers, data=self.data) 26 | token = re.search('(.*?)', res.text, re.M | re.I) 27 | # print("token值等于", token.group(1)) 28 | token = token.group(1) 29 | return token 30 | 31 | 32 | # r = cookie().login() 33 | -------------------------------------------------------------------------------- /Common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oscear/autoTest-for-api/f6de611c1c69ffc35dcdda7ffb2936b3a818ef69/Common/__init__.py -------------------------------------------------------------------------------- /Common/xmltest.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from xml.etree import ElementTree 3 | qq_str = '1203069758' 4 | url_str ='http://www.webxml.com.cn//webservices//qqOnlineWebService.asmx//qqCheckOnline?qqCode=%s'%qq_str 5 | text_str = requests.get(url_str) 6 | text_str.encoding='utf-8' 7 | #解析xml格式内容,将字符串转为特殊的对象 8 | 9 | 10 | node = ElementTree.XML(text_str.text) 11 | print("node等于",node.text) 12 | 13 | tree = ElementTree.fromstring(text_str.content) 14 | print("node2等于",tree.text) -------------------------------------------------------------------------------- /Common/yhapi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : OSCar 4 | # @Time : 2021/11/5 16:46 5 | # @File : yhapi.py 6 | # @Project : myblog 7 | # @Mail : 1203069758@qq.com 8 | import requests 9 | from xml.etree import ElementTree as ET 10 | import json 11 | import xmltodict 12 | 13 | url = "http://127.0.0.1:999/bi/api?action=shareDB&token=" 14 | token = "80D4B9499F3FB36E20F5ECD45E2D8FD3" 15 | url2 = url + token 16 | data = {'xmlData': ''' 17 | 18 | 19 | 20 | sharedb 21 | db 22 | 23 | 24 | 25 | 26 | user 27 | a 28 | 29 | 30 | 31 | ''' 32 | } 33 | headers = {'content-type': 'application/xml'} 34 | 35 | res = requests.post(url2, headers=headers, data=data) 36 | print('res的结果是', res.text) 37 | 38 | # resDict = json.loads(res.content,strict=False) 39 | 40 | resDict2 = xmltodict.parse(res.text) 41 | # print('resdict等于', resDict) 42 | print('resdict等于', resDict2) 43 | print('resdict类型', type(resDict2)) 44 | print('resdict类型', resDict2['results']['result']['level']) 45 | print('resdict类型', resDict2['results']['result']['message']) 46 | -------------------------------------------------------------------------------- /Config/conf.ini: -------------------------------------------------------------------------------- 1 | [url] 2 | ip=127.0.0.1 3 | port=999 4 | 5 | [database] 6 | aa=1 7 | 8 | [sql] 9 | bb=2 10 | 11 | -------------------------------------------------------------------------------- /Logs/log.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2021-4-22 11:30 3 | # @Author : 奥斯卡 4 | # @Email : 1203069758@qq.com 5 | # @File : log.py 6 | import logging 7 | import time 8 | import os 9 | import getcwd 10 | 11 | 12 | def get_log(logger_name): 13 | # 创建一个logger 14 | logger = logging.getLogger(logger_name) 15 | logger.setLevel(logging.INFO) 16 | 17 | # 设置日志存放路径,日志文件名 18 | # 获取本地时间,转换为设置的格式 19 | rq = time.strftime('%Y%m%d%H%M', time.localtime(time.time())) 20 | # 设置所有日志和错误日志的存放路径 21 | path = getcwd.get_cwd() 22 | # 通过getcwd.py文件的绝对路径来拼接日志存放路径 23 | all_log_path = os.path.join(path, 'Logs/All_Logs/') 24 | error_log_path = os.path.join(path, 'Logs/ERROR_Logs/') 25 | # 设置日志文件名 26 | all_log_name = all_log_path + rq + '.log' 27 | error_log_name = error_log_path + rq + '.log' 28 | 29 | # 创建handler 30 | # 创建一个handler写入所有日志 31 | fh = logging.FileHandler(all_log_name) 32 | fh.setLevel(logging.INFO) 33 | # 创建一个handler写入错误日志 34 | eh = logging.FileHandler(error_log_name) 35 | eh.setLevel(logging.ERROR) 36 | # 创建一个handler输出到控制台 37 | ch = logging.StreamHandler() 38 | ch.setLevel(logging.INFO) 39 | 40 | # 定义日志输出格式 41 | # 以时间-日志器名称-日志级别-日志内容的形式展示 42 | all_log_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 43 | # 以时间-日志器名称-日志级别-文件名-函数行号-错误内容 44 | error_log_formatter = logging.Formatter( 45 | '%(asctime)s - %(name)s - %(levelname)s - %(module)s - %(lineno)s - %(message)s') 46 | # 将定义好的输出形式添加到handler 47 | fh.setFormatter(all_log_formatter) 48 | ch.setFormatter(all_log_formatter) 49 | eh.setFormatter(error_log_formatter) 50 | 51 | # 给logger添加handler 52 | logger.addHandler(fh) 53 | logger.addHandler(eh) 54 | logger.addHandler(ch) 55 | return logger 56 | 57 | 58 | log1 = get_log("YongHongApi") 59 | 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Python+Requests+PyTest+Excel+Allure接口自动化测试实战 2 | 3 | #### 目录说明 4 | - **common** 5 | 公用的基础方法,连接数据库执行SQL语句、获取多层嵌套dict中某一个key的值等 6 | - **config** 7 | 存放配置文件 8 | - **Data** 9 | 存放测试用户和测试相关数据 10 | - **Logs** 11 | 存放日志 12 | - **test_pytest** 13 | 测试用例执行目录,里面只写具体的测试用例代码,测试用例要以test_开头或者以_test结尾,测试用例不改变的情况下,这里面的代码编写好后一般不需要进行修改 14 | - **rdFile** 15 | 读取各类文件,目前只包含有excel、ini,后续扩充 16 | - **conftest.py** 17 | 与conftest.py同目录的文件执行时都会执行该文件 18 | 目前创建driver对象和运行失败截图的放在此文件中的 19 | * **Report** 20 | 保存运行结果的目录,用的是allure来生成的报告,多个json文件: 21 | 也可通过jenkins集成allure报告,可看到执行结果的趋势 22 | -------------------------------------------------------------------------------- /data/a.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oscear/autoTest-for-api/f6de611c1c69ffc35dcdda7ffb2936b3a818ef69/data/a.xls -------------------------------------------------------------------------------- /data/aBAK.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oscear/autoTest-for-api/f6de611c1c69ffc35dcdda7ffb2936b3a818ef69/data/aBAK.xls -------------------------------------------------------------------------------- /data/json/1.py: -------------------------------------------------------------------------------- 1 | a='''王老吉''' 2 | res='''{'customData': {}, 'data': [{'jbld': '王老吉', 'fkdwNum': 1, 'bh': '000154b80c8646f4913ebd11286c3fad', 'jbrq': '2021-09-25', 'zjsdwNum': 1, 'cjsj': '2021-04''' 3 | if a in res: 4 | print('通过') -------------------------------------------------------------------------------- /getcwd.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2021-4-22 11:27 3 | # @Author : 奥斯卡 4 | # @Email : 1203069758@qq.com 5 | # @File : getcwd.py 6 | import os 7 | def get_cwd(): 8 | path = os.path.dirname(os.path.abspath(__file__)) 9 | #当前文件的绝对路径 10 | return path 11 | 12 | os.getcwd()#获取当前目录 13 | os.path.dirname(__file__)#获取当前目录 14 | os.path.dirname(os.path.dirname(__file__))#获取当前目录的上一级 -------------------------------------------------------------------------------- /rdfile/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2021-4-27 19:45 3 | # @Author : 奥斯卡 4 | # @Email : 1203069758@qq.com 5 | # @File : __init__.py 6 | -------------------------------------------------------------------------------- /rdfile/rdExcel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : OSCar 4 | # @Time : 2021/11/5 18:53 5 | # @File : rdExcel.py 6 | # @Project : myblog 7 | # @Mail : 1203069758@qq.com 8 | 9 | import xlrd 10 | import getcwd 11 | import os 12 | 13 | path = getcwd.get_cwd() 14 | 15 | class read_excel(): 16 | 17 | def __init__(self, xls, sheet): 18 | ''' 19 | @param xls: 传入excel的名字 20 | @param sheet: 传入sheet的名字,注意大小写 21 | ''' 22 | self.excelpath = os.path.join(path, 'data/' + xls) 23 | # 打开excel 24 | self.book = xlrd.open_workbook(self.excelpath) 25 | # 获取excel 26 | self.sheet = self.book.sheet_by_name(sheet) 27 | 28 | # 以列表形式读取出所有数据 29 | def getExceldatas(self): 30 | data = [] 31 | title = self.sheet.row_values(0) 32 | # 0获取第一行也就是表头 33 | print("用例总条数为", self.sheet.nrows) 34 | for row in range(0, self.sheet.nrows): # 从第一行包括表头开始获取 35 | row_value = self.sheet.row_values(row) 36 | data.append(dict(zip(title, row_value))) # 将读取出每一条用例作为一个字典存放进列表 37 | return data 38 | 39 | 40 | def get_nrows(self): 41 | '''获取列表行数''' 42 | return self.sheet.nrows 43 | 44 | 45 | 46 | # hc = OperationExcel('a.xls','Sheet1') 47 | # print(hc.getExceldatas()) 48 | # print(type(hc.getExceldatas())) 49 | # print() 50 | # 51 | # for i in range(1, hc.get_nrows()): 52 | # num = i - 1 53 | # print('num的顺序为', num) 54 | # host = hc.getExceldatas()[num]['Host'] 55 | # print(host) -------------------------------------------------------------------------------- /rdfile/rdini.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2021-4-2 17:02 3 | # @Author : 奥斯卡 4 | # @Email : 1203069758@qq.com 5 | # @File : Readini.py 6 | import configparser 7 | import os 8 | import getcwd 9 | 10 | 11 | path = getcwd.get_cwd() 12 | 13 | 14 | def read_ini(section, name): 15 | rf = configparser.ConfigParser() 16 | inipath = os.path.join(path, 'Config/conf.ini') 17 | # rf.read("..\Config\conf.ini") 18 | rf.read(inipath) 19 | key = rf.get(section, name) 20 | return key 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /report/allure_result/32425007-3e44-4707-824b-5eb16ca4f7b7-result.json: -------------------------------------------------------------------------------- 1 | {"name": "test_api", "status": "passed", "attachments": [{"name": "log", "source": "dea0c60d-318d-4f99-a7b0-1d892cb8ca32-attachment.txt", "type": "text/plain"}], "start": 1636725331773, "stop": 1636725331895, "uuid": "3d5d4a55-297b-4d35-9377-49aec3c7a971", "historyId": "cd7303856ea89c1d705a4a4324eb4172", "testCaseId": "b04a447cc074f51f2c2bf27f3180eb4c", "fullName": "test_pytest.test_two.TestYongHong#test_api", "labels": [{"name": "parentSuite", "value": "test_pytest"}, {"name": "suite", "value": "test_two"}, {"name": "subSuite", "value": "TestYongHong"}, {"name": "host", "value": "LAPTOP-SL67I6GL"}, {"name": "thread", "value": "12608-MainThread"}, {"name": "framework", "value": "pytest"}, {"name": "language", "value": "cpython3"}, {"name": "package", "value": "test_pytest.test_two"}]} -------------------------------------------------------------------------------- /report/allure_result/c6868cd2-d85d-4095-9c18-c0e73e7c531d-container.json: -------------------------------------------------------------------------------- 1 | {"uuid": "47a2513a-513b-48f5-a301-51f192b44dff", "children": ["3d5d4a55-297b-4d35-9377-49aec3c7a971"], "befores": [{"name": "xunit_setup_class_fixture_TestYongHong", "status": "passed", "start": 1636725331767, "stop": 1636725331768}], "afters": [{"name": "xunit_setup_class_fixture_TestYongHong::0", "status": "passed", "start": 1636725331895, "stop": 1636725331896}], "start": 1636725331767, "stop": 1636725331896} -------------------------------------------------------------------------------- /report/allure_result/dea0c60d-318d-4f99-a7b0-1d892cb8ca32-attachment.txt: -------------------------------------------------------------------------------- 1 | INFO YongHongApi:test_two.py:51 正在测试的接口编号是:1.0,名称是分享报告 2 | ERROR YongHongApi:test_two.py:53 当前接口运行有误,其编号是2.0 3 | INFO YongHongApi:test_two.py:51 正在测试的接口编号是:3.0,名称是替换数据集 4 | INFO YongHongApi:test_two.py:51 正在测试的接口编号是:4.0,名称是新增用户 5 | INFO YongHongApi:test_two.py:51 正在测试的接口编号是:5.0,名称是删除用户 6 | INFO YongHongApi:test_two.py:51 正在测试的接口编号是:6.0,名称是新增admin 7 | INFO YongHongApi:test_two.py:51 正在测试的接口编号是:7.0,名称是删除admin -------------------------------------------------------------------------------- /test_pytest/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : OSCar 4 | # @Time : 2021/11/5 17:49 5 | # @File : __init__.py.py 6 | # @Project : myblog 7 | # @Mail : 1203069758@qq.com 8 | -------------------------------------------------------------------------------- /test_pytest/test_api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : OSCar 4 | # @Time : 2021/11/5 17:49 5 | # @File : test_api.py 6 | # @Project : apitest 7 | # @Mail : 1203069758@qq.com 8 | import os 9 | import pytest 10 | from rdfile.rdini import read_ini 11 | from rdfile.rdExcel import read_excel 12 | from Common.Base_test import ApiRequest 13 | from Common.Login import cookie 14 | from Logs.log import log1 15 | 16 | 17 | class TestYongHong: 18 | 19 | 20 | def setup_class(self): 21 | print('开始测试') 22 | 23 | 24 | def teardown_class(self): 25 | print('结束测试') 26 | 27 | def test_api(self): 28 | token = cookie().login() 29 | ip = read_ini('url', 'ip') 30 | port = read_ini('url', 'port') 31 | datatable = read_excel('a.xls', 'Sheet1') 32 | data = datatable.getExceldatas() 33 | # 读出来是个列表内包含字典 34 | 35 | try: 36 | # 因为第一行是excel表头,从第二行开始获取 37 | for i in range(1, datatable.get_nrows()): 38 | path = data[i]['path'] 39 | method = data[i]['method'] 40 | headers = data[i]['headers'] 41 | body = data[i]['data'] 42 | # 前面是从excel取得值 43 | url = 'http://' + ip + ':' + port + path + token 44 | # print('url是', url) 45 | # 拼接url 46 | checkpoint = data[i]['check'] 47 | res = ApiRequest().send_requests(method=method, url=url, headers=eval(headers), data=eval(body)) 48 | # print('res等于', res.text) 49 | log1.info("正在测试的接口编号是:%s,名称是%s" % (data[i]['c_bh'], data[i]['name'])) 50 | assert checkpoint in res.text, '响应内容错误' 51 | except Exception: 52 | log1.error("有接口预期不符") 53 | 54 | 55 | if __name__ == '__main__': 56 | # os.system(r'del /f /q ..\Logs\ALL_Logs') 57 | # os.system(r'del /f /q ..\Logs\ERROR_Logs') # 先删除旧日志 58 | pytest.main(['-s', '-v', 'test_api.py', '--clean-alluredir', '--alluredir', '../report/allure_result']) 59 | os.system(r'allure serve ../report/allure_result') 60 | # 等同于在dos执行:pytest -s -v --clean-alluredir --alluredir ../report/allure_result 61 | # 等同于在dos执行: allure serve ../report/allure_result 62 | -------------------------------------------------------------------------------- /test_pytest/test_two.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : OSCar 4 | # @Time : 2021/11/5 17:49 5 | # @File : test_api.py 6 | # @Project : apitest 7 | # @Mail : 1203069758@qq.com 8 | import os 9 | import pytest 10 | import xmltodict 11 | from rdfile.rdini import read_ini 12 | from rdfile.rdExcel import read_excel 13 | from Common.Base_test import ApiRequest 14 | from Common.Login import cookie 15 | from Logs.log import log1 16 | 17 | 18 | class TestYongHong: 19 | 20 | 21 | def setup_class(self): 22 | print('开始测试') 23 | 24 | 25 | def teardown_class(self): 26 | print('结束测试') 27 | 28 | def test_api(self): 29 | token = cookie().login() 30 | ip = read_ini('url', 'ip') 31 | port = read_ini('url', 'port') 32 | datatable = read_excel('a.xls', 'Sheet1') 33 | data = datatable.getExceldatas() 34 | # 读出来是个列表内包含字典 35 | 36 | 37 | # 因为第一行是excel表头,从第二行开始获取 38 | for i in range(1, datatable.get_nrows()): 39 | path = data[i]['path'] 40 | method = data[i]['method'] 41 | headers = data[i]['headers'] 42 | body = data[i]['data'] 43 | # 前面是从excel取得值 44 | url = 'http://' + ip + ':' + port + path + token 45 | checkpoint = data[i]['check'] 46 | 47 | try: 48 | res = ApiRequest().send_requests(method=method, url=url, headers=eval(headers), data=eval(body)) 49 | resDict = xmltodict.parse(res.text) 50 | # assert resDict['results']['result']['level'] == '1' 51 | assert checkpoint in res.text, '响应内容错误' 52 | log1.info("正在测试的接口编号是:%s,名称是%s" % (data[i]['c_bh'], data[i]['name'])) 53 | except Exception: 54 | log1.error("当前接口运行有误,其编号是%s" % data[i]['c_bh']) 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | if __name__ == '__main__': 63 | # os.system(r'del /f /q ..\Logs\ALL_Logs') 64 | # os.system(r'del /f /q ..\Logs\ERROR_Logs') # 先删除旧日志 65 | pytest.main(['-s', '-v', 'test_two.py', '--clean-alluredir', '--alluredir', '../report/allure_result']) 66 | os.system(r'allure serve ../report/allure_result') 67 | # 等同于在dos执行:pytest -s -v --clean-alluredir --alluredir ../report/allure_result 68 | # 等同于在dos执行: allure serve ../report/allure_result 69 | --------------------------------------------------------------------------------