# todo 2/22
# 自动化测试框架 测试报告 写用例 运行用例 日志/失败截图 筛选用例/重运行功能 方便冒烟和回归的分别
# unittest pytest 区别 重新运行失败的用例 筛选用例 加载测试用例
# 设置pytest运行模式 已test Test 开头的函数和类
# 表达用例
# unittest 定义一个类 继承unittest.TestCase
# pytest 设置pytest运行模式 已test Test 开头的函数和类
# 断言 self.assert assert 表达式 为true
# 收集用例 unittest TestLoader.discover类 TestSuite pytest 自动收集用例
# 命令行 当前目录 pytest 收集 。py test_ _test结尾 Test开头的类 test开头的函数
# 执行顺序 文件名 ascll码顺序 文件内是按代码的先后顺序执行
# fixture 前置后置
# unittest setup、teardown setupclass teardownclass
# pytest function class module session
# 插件 pytest 700+ html报告 allure 报告 重运行
# 筛选用例 打标记 目的筛选回归和冒烟用例 命令行运行过滤 pytest -s -v -m maoyan
# 先注册标签 在@pytest.mack.maoyan
# 模块打标记 pytestmark = pytest.mark.maoyan
# todo 2/11
# todo 上传文件操作封装
import pywintypes
#import pythoncom # Uncomment this if some other DLL load will fail
import win32gui
import win32con
# 前提Windows上传窗口已经出现,sleep1-2秒等待弹窗出现
def upload(filepath,brower_type = "chrome"):
if brower_type == "chrome":
title = "打开"
else:
title = ""
# 找元素
# 一级窗口
dialog = win32gui.FindWondow("#32770",title)
comboboxex32 = win32gui.FindWondowEx(dialog,0,"ComboBoxEx32",None) # 二级
ComboBox = win32gui.FindWondowEx(comboboxex32,0,"ComboBox",None) # 三级
# 编辑控件 四级
edit= win32gui.FindWondowEx(ComboBox,0,"Edit",None)
# 打开按钮
button = win32gui.FindWondowEx(dialog,0,"Button","打开(&0)")
# 发送文件路径
win32gui.SendMessage(edit,win32con.WM_SETTEXT,None,filepath)
# 点击打开按钮
win32gui.SendMessage(dialog,win32con.WM_COMMAND,1,button)
time.sleep(2)
upload("d/asd")
# todo 2/16
# 优化框架 写用例
# 用例多/维护遇到的问题
# 稳定的功能 自动化 比较依赖页面 应用在回归和冒烟 重复执行的用例
# 回归 接口自动化(快速) + UI测试(正向+部分逆向场景)
# 可扩展性/可维护性/可回溯性 用例执行失败有日志什么的 复现
# 分析业务\项目状态\功能\核心业务\迭代周期
# 那些功能自动化
# 设计思想/框架
# 稳定性很重要 调试 用例通过率 脚本的bug 重试机制 Jenkins-持续集成
# 执行自动化用例 定时执行 执行频率
# 自动化测试环境 集成测试环境 预发布环境 生产环境 执行场景 冒烟 回归
# 测试报告
# 维护阶段 增加用例 优化脚本 提高稳定性 优化执行时间 修改用例、代码
# todo 2/17
# 测试数据明确 预期结果与实际结果的断言
# 图片比对 airtest
# pageobject 页面类 有元素定位属性 和操作元素方法
# 用例中直接调用页面操作 业务逻辑(+测试数据)
# 通过打断点 解决调试问题 调试技巧 哪里有问题 哪里打断点
# 定位快速消失的元素 在sources中点击暂停执行
# 用例的独立性 稳定性 重要 再考虑效率 用setup前置
# 元素地位 操作 测试用例 业务逻辑 测试数据
# 测试环境先配置好
# 场景流程自动化测试 每一个场景为一个用例
# todo 2/19
# 自动化用例设计原则 重复、繁琐的用例 冒烟和回归测试 数据清理
# 自动化测试框架
# 日志 记录用例执行过程 失败截图 用例执行不通过 截图当前页面 测试报告
# basepage 对webdriver进行二次封装 selenium webdriver api
# todo 2/6
# 定位选中可以会变化的元素要注意
# 悬浮隐藏元素 右键 检查
# 页面跑不过代码 需要等待
# sleep 辅助作用
# 窗口切换
# 窗口句柄
wins = driver.window_handles
# 切换新窗口
driver.switch_to.window(wins[-1])
# 当前窗口句柄
driver.current_window_handle
# iframe 是标签对 为什么
# 切换iframe
driver.switch_to.frame()
# 切换方式
# Switches focus to the specified frame, by index, name, or webelement.
# name属性
driver.switch_to.frame('frame_name') # 常用
driver.switch_to.frame(1)
driver.switch_to.frame(driver.find_elements_by_tag_name("iframe")[0])
# 回到默认HTML
driver.switch_to.default_content()
# 回到上一级iframe
driver.switch_to.parent_frame()
# 什么情况切换 窗口 iframe
# 切换alert弹框
alert = driver.switch_to.alert
alert.text
alert.dismiss()
alert.accept()
alert.send_keys()
# 打开网页有弹窗怎么办
# actionchains 鼠标操作 动作链
# webelement对象 locator元素定位
# 悬浮 mover
from selenium.webdriver.common.action_chains import ActionChains
# 将执行鼠标动作先放到一个列表当中,perfect() 执行鼠标动作
# todo 2/07
# 实例化
ac = ActionChains(driver)
ac.move_to_element(element).perform()
# 双击 拖拽
# Select类处理 下拉框处理
Select(element).select_by_visible_text()
Select(element).select_by_index()
Select(element).select_by_value()
# 键盘操作 特殊键
from selenium.webdriver.common.keys import Keys
element.send_keys("asda",Keys.ENTER)
# todo 2/08
# Alert类 actionchains select keys类
# 滚动条 元素顶端与窗口顶部对齐 上面
driver.execute_script(script="arguments[0].scrollIntoView();",element) # arguents 接收外部的参数
# arguments[0].scrollIntoView(false) 元素底部与窗口底部对齐
# 移动到页面底部 “window.scrollTo(0,document.body.scrollHeight)”
# 移动到页面顶部 “window.scrollTo(document.body.scrollHeight,0)”
# 日期框操作
pha_js = "var a =argument[0];a.readonly=false;a.value= '2021/2/8'"
driver.execute_script(pha_js,element)
# todo 2/3
# 窗口最大化
driver.maximize_window()
# 回到上一个页面
driver.back()
# 前进到下一个页面
driver.forward()
# 刷新
driver.refresh()
# 强制等待
time.sleep(2)
# 关闭当前窗口
driver.close()
# 退出会话 关闭浏览器 关闭Chromedriver进程
driver.quit()
# 页面组成 HTML页面内容+CSS内容呈现,布局设置,字体大小颜色+javascript
# 元素=标签 = 内容
# 调用属性
# id --在这个页面绝对唯一
# class
# style -css 外部 内部
# name
# 图片 上传文件
# 表格toble th 列表ul li 下拉列表select
# iframe
# 链接 a
# 图片 img
# input
# textarea 富文本
# alert弹窗
# 八大元素定位方法
# 只根据元素的一个属性 id name class_name tag_name
# a元素 link_text partial_link_text
# 多个属性组合或关系
# xpath css_selector
# webelement 对象
# id
element = driver.find_element_by_id()
element.is_selected()
element.find_element_by_id()
# class
driver.find_element_by_class_name()
driver.find_elements_by_class_name()
# name
driver.find_elements_by_class_name()
# tag
driver.find_element_by_tag_name("input")
# link_text
driver.find_element_by_link_text("地图")
driver.find_element_by_partial_link_text("地")
# send_keys
element.send_keys("xiaoyu")
# xpath--用的多 处理方案多一点 测试
# 绝对定位 继承顺序、兄弟位置顺序 /../../
# 相对定位 F12 critical+F
# / 父子关系 // 后代关系
# //标签名[@属性名=“属性值”]//二级元素
# CSS_selector
# todo 2/5
# //*
# 文本匹配 //a[text()="公告"]
# 包含 //标签[contains(@属性,text(),值)]
# 逻辑 组合 and or
# //标签[@属性=值 and @属性=值]
# 轴运算: 关系
# 通过同级找 通过后代元素定位表达式
# 已定位元素/轴元素::需要定位元素
# //p[@title="写亿"]/preceding-sibling::p[@class="stuno"] 当前节点之前的兄弟节点
# ancestor 祖先节点 parent父节点 following-sibling 后面的节点
# 下标/js 标签名[4]
driver.find_element_by_css_selector("input#class.id")
driver.find_element(By.XPATH, "p[@yu=‘dasd’]")
# 输入操作
driver.find_element(By.ID, "sDA").send_keys("ADS")
# 点击操作
element.click()
# 强制等待
time.sleep(3)
# 隐式等待
driver.implicitly_wait(10) # timeoutexception nosuchelementexption
# 显示等待
from selenium.webdriver.support.wait import WebDriverWait # 等待
from selenium.webdriver.support import expected_conditions as EC # 判断条件
loc = (By.XPATH, "//p[@sda=‘asd’]")
WebDriverWait(driver, 15, 0.5).until(EC.visibility_of_element_located(loc)) # 元素可见
# locator 元素定位
# todo 1/30
# 分时天月 星期几 * , -,/
# mock 模拟测试数据
res = Mock(return_value=7)
# mock 服务 JavaScript mock服务 java mock
# postman 注册mock 服务
# todo 1/31
# 个人简介
# 简历技能 突出优势() 精简 每行字数15字以内 精通 熟悉 掌握 了解
# 工作经历 负责内容 成就 自动化项目 突出自动化内容和细节
# 项目经验 re正则匹配 用例关联
# 自我评价 团队 沟通 开朗 学习能力
# todo 2/1
# 接口参数 动态参数 增加扩展性 接口依赖 Context property 重点
# 验证码接口一般不会遇到 全球鹰
# 接口文档 开发确认
# postman 和代码进行接口测试区别
# postman 不好管理和维护 拓展性不强 数据库校验 自动断言 需要插件 会javascrip 是一个封闭的系统
# 接口独立性 接口依赖
# 测试用例的设计
# 第三方框架的区别 要懂原理
# middleware 中间件 项目相关的
# todo 2/2
# 接口自动化和web自动化的区别 处理数据 和 web自动化 用户接口
# web自动化 重复点点点的就不用手了就用代码就行了
# 代码selenium 中间件 Chromedriver 浏览器 ie/Firefox/chrome
# webdirver 库 pip install -U selenium
# grid 分布式
# seleniium 工作原理 webdriver api都是一个http通信接口 通信协议jsonwireprotocol
# 通信流程 发送http请求给driver
# driver驱动浏览器
# driver返回结果给代码
from selenium import webdriver
# 启动Chromedriver 建立连接 会话ID 打开浏览器
# 与第三方的资料建立连接 需要异常处理
driver = webdriver.Chrome()
# selenium 原理 源码解析
# 打开百度
driver.get("https://www.baidu.com/")
# 代码 -http通信(json格式) - xxxdriver(驱动程序)-浏览器
# 每一个对网页的操作都是一个接口 json格式
# todo 1/25
# jenkins 自动化测试 配置jdk
# 新建任务 新增项目
# todo 1/26
# jenkins windows 批处理
# 文件放在workspace
# 发送邮件
# 开启stmp服务
# 定时构建
读取配置文件
from configparser import ConfigParser
class ConfigHandler(ConfigParser):
def __init__(self, file, encoding="utf-8"):
super().__init__()
self.read(file, encoding=encoding)
if __name__ == '__main__':
config = ConfigHandler(file)
config.get(
section=section,
option=option
)
读取数据库
import pymysql
from pymysql.cursors import DictCursor
from api_unittest_excel.common.yaml_handler import yaml_data
class DBHandler:
def __init__(self, host, port, user, password, database,
charset=None, cursorclass=DictCursor, **kwargs):
"""初始化"""
self.conn = pymysql.connect(host=host,
port=port,
user=user,
password=password,
database=database,
charset=charset,
cursorclass=cursorclass,
**kwargs) # 返回字典数据
self.cursor = self.conn.cursor()
def query(self, sql, args=None, one=True):
self.cursor.execute(sql, args=args)
# 事务提交
self.conn.commit()
if one:
return self.cursor.fetchone()
else:
return self.cursor.fetchall()
def close(self):
self.cursor.close()
self.conn.close()
db = DBHandler(yaml_data["database"]["host"],
yaml_data["database"]["port"],
yaml_data["database"]["user"],
yaml_data["database"]["password"],
yaml_data["database"]["database"],
yaml_data["database"]["charset"])
读取excel表格
from openpyxl import load_workbook
from openpyxl.worksheet.worksheet import Worksheet
from api_unittest_excel.config.setting import config
class ExcelHandler:
"""操作Excel表格"""
def __init__(self, file):
"""初始化文件名"""
self.file = file
def open_sheet(self, sheet_name) -> Worksheet: # 函数的返回时worksheet 函数注解
"""打开表单"""
wb = load_workbook(self.file)
sheet = wb[sheet_name]
wb.close()
return sheet
def read_hander(self, sheet_name):
"""读取表头数据"""
sheet = self.open_sheet(sheet_name)
hander_data = sheet[1]
hander_list_data = []
for cell in hander_data:
hander_list_data.append(cell.value)
return hander_list_data
def read_excel(self, sheet_name):
"""读取所有的数据"""
sheet = self.open_sheet(sheet_name)
rows = list(sheet.rows)
data = []
for row in rows[1:]:
row_data = []
for cell in row:
row_data.append(cell.value)
dict_data = dict(zip(self.read_hander(sheet_name), row_data)) # zip用法 字典标题和数据一一对应 dict 字典转换
data.append(dict_data)
return data
@staticmethod
def write_excel(file, sheet_name, row, column, data):
"""写入Excel数据"""
wb = load_workbook(file)
sheet = wb[sheet_name]
sheet.cell(row, column).value = data
wb.save(file)
wb.close()
excel = ExcelHandler(config.data_path)
from logging import Logger, Formatter, FileHandler, StreamHandler
from api_unittest_excel.common.yaml_handler import yaml_data
class LoggerHandler(Logger):
def __init__(self, name="root", level="DEBUG", fmt='%(asctime)s %(filename)s : %(levelname)s %(message)s',
file=None):
super().__init__(name)
self.setLevel(level)
fmt = Formatter(fmt)
if file:
file_handler = FileHandler(file, encoding="utf-8")
file_handler.setLevel(level)
file_handler.setFormatter(fmt)
# 添加handler处理器
self.addHandler(file_handler)
stream_handler = StreamHandler()
stream_handler.setLevel(level)
stream_handler.setFormatter(fmt)
self.addHandler(stream_handler)
logger = LoggerHandler(yaml_data["logger"]["name"],
level=yaml_data["logger"]["level"],
file=yaml_data["logger"]["file"])
# request_handler.py
import requests
class RequestHandler:
def __init__(self):
self.session = requests.Session()
def visit(self, method, url, params=None, data=None, json=None, headers=None, **kwargs):
res = self.session.request(method, url, params=params, data=data, json=json, headers=headers, **kwargs)
try:
return res.json()
except ValueError:
print("not json")
def close(self):
self.session.close()
# yaml_handler.py
import yaml
from api_unittest_excel.config.setting import config
class YamlHandler:
def __init__(self, file):
self.file = file
def read(self, encoding="utf-8"):
with open(file=self.file, encoding=encoding) as f:
data = yaml.load(f, Loader=yaml.FullLoader)
return data
yaml_data = YamlHandler(config.config_yaml_path).read()
logger:
name: xiaoyu
level: WARNING
file: log.txt
database:
host: "120.28.25"
port: 111
user: "fut2"
password: "123456"
database: "fut2l2n"
charset: "utf8"
user:
mobile_phone: "137122234"
pwd: "1234278"
setting.py
import os
class Config:
# 项目路径
root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 测试数据路径
data_path = os.path.join(root_path, "test_data/test_cases.xlsx")
# 测试用例路径
test_cases_path = os.path.join(root_path, "test_cases")
# 测试报告路径
reports_path = os.path.join(root_path, "reports")
# 配置文件yaml路径
config_yaml_path = os.path.join(root_path, "config", "config.yaml")
class DevConfig(Config):
# 域名
host = "http://120.78.128.25:8766/futureloan"
config = DevConfig()
testrunner
ddt
if isinstance(v, dict):
test_name = mk_test_name(name, v["case_name"], i)
helper.py
import re
from jsonpath import jsonpath
from api_unittest_excel.common.db_handler import db
from api_unittest_excel.common.request_handler import RequestHandler
from api_unittest_excel.common.yaml_handler import yaml_data
from api_unittest_excel.config.setting import config
import random
def generate_mobile_phone():
"""生成一个手机号码"""
phone_number = "1" + random.choice(["3", "5", "7", "8", "9"])
for i in range(9):
num = random.randint(1, 9)
phone_number += str(num)
return phone_number
# 登录成功
def login():
req = RequestHandler()
res = req.visit("post",
config.host + "member/login",
json=yaml_data["user"],
headers={"X-Lemonban-Media-Type": "Lemonban.v2"})
return res
# 替换字符串变量
def replace_labal(target):
re_pattern = r"#(.*?)#"
while re.findall(re_pattern, target):
key = re.search(re_pattern, target).group(1) # 获取一个#***# 变成***
target = re.sub(re_pattern, str(getattr(Context(), key)), target, 1) # getattr 获取动态变量类属性 替换字符串
return target
class Context:
"""保存临时变量"""
# 获取load_id
@property # 实例属性
def load_id(self):
loan = db.query("select * from loan where status=2 limit 100;")
db.close()
return loan["id"]
# 获取token
@property
def token(self):
"""保存token和member_id信息"""
json_data = login()
token = jsonpath(json_data, "$..token")[0]
token_type = jsonpath(json_data, "$..token_type")
t = " ".join([token_type, token]) # 传入列表
return token
# 获取member_id
@property
def member_id(self):
json_data = login()
member_id = jsonpath(json_data, "$..id")[0]
return member_id
if __name__ == '__main__':
print(Context().token)
## qcd_db_handler.py
from api_unittest_excel.common.yaml_handler import yaml_data
class MyDBHandler(DBHandler):
def __init__(self):
super(MyDBHandler, self).__init__(yaml_data["database"]["host"],
yaml_data["database"]["port"],
yaml_data["database"]["user"],
yaml_data["database"]["password"],
yaml_data["database"]["database"],
yaml_data["database"]["charset"])
# 带参数的初始化
## qcd_logger.py
from api_unittest_excel.common.logger_handler import LoggerHandler
from api_unittest_excel.common.yaml_handler import yaml_data
class MyLogger(LoggerHandler):
def __init__(self):
super().__init__(yaml_data["logger"]["name"],
level=yaml_data["logger"]["level"],
file=yaml_data["logger"]["file"])
# 初始化
logger = MyLogger()
##reports
test_01_register.py
import json
import unittest
from api_unittest_excel.common.db_handler import DBHandler, db
from api_unittest_excel.common.excel_handler import excel
from api_unittest_excel.common.helper import generate_mobile_phone
from api_unittest_excel.common.logger_handler import logger
from api_unittest_excel.common.request_handler import RequestHandler
from api_unittest_excel.common.yaml_handler import yaml_data
from api_unittest_excel.config.setting import config
from api_unittest_excel.lib.ddt import ddt, data
@ddt
class TestRegister(unittest.TestCase):
# 读取数据
test_datas = excel.read_excel("register")
def setUp(self) -> None:
self.req = RequestHandler()
def tearDown(self) -> None:
self.req.close()
db.close()
@data(*test_datas)
def test_register(self, test_data):
# 注册成功
if "new_phone" in test_data["json_data"]:
while True:
phone = generate_mobile_phone()
# 如果手机号码在数据库,继续生成手机号码,不在数据库,则替换
phone2 = db.query("select * from member where mobile_phone=%s", args=[phone])
if phone2:
continue
else:
# 替换数据手机号码
test_data["json_data"] = test_data["json_data"].replace("#new_phone#", phone)
break
# # 替换数据手机号码
# test_data["json_data"] = test_data["json_data"].replace("new_phone", phone)
# 已注册
if "#exist_phone#" in test_data["json_data"]:
phone = self.db.query("select mobile_phone from member")["mobile_phone"]
test_data["json_data"] = test_data["json_data"].replace("#exist_phone#", phone)
logger.info("*" * 80)
logger.info("执行测试用例:{}{}".format(test_data["case_id"], test_data["case_name"]))
# 访问接口,得到实际结果
res = self.req.visit(test_data["method"],
config.host + test_data["url"],
headers=json.loads(test_data["headers"]),
json=json.loads(test_data["json_data"]))
try:
self.assertEqual(res["code"], test_data["excepted_result"])
# 测试结果写入Excel
excel.write_excel(config.data_path, "register", test_data["case_id"] + 1, 9, "pass")
# except AssertionError as e:
except Exception as e:
logger.error("用例断言失败:{}".format(e))
# 测试数据写回
excel.write_excel(config.data_path, "register", test_data["case_id"] + 1, 9, "fail")
# 手动抛出异常 让用例执行失败
raise e
test_02_login.py
import json
import unittest
from api_unittest_excel.common.excel_handler import excel
from api_unittest_excel.common.request_handler import RequestHandler
from api_unittest_excel.lib.ddt import ddt, data
test_datas = excel.read_excel("login")
@ddt
class TestLogin(unittest.TestCase):
def setUp(self) -> None:
self.req = RequestHandler()
def tearDown(self) -> None:
self.req.close()
@data(*test_datas)
def test_login(self, test_data):
res = self.req.visit(method=test_data.get("method"), url=test_data.get("url"),
headers=json.loads(test_data.get("headers")),
json=json.loads(test_data.get("json_data")))
self.assertEqual(res, test_data.get("expected_result"))
#test_03_recharge.py
import json
import unittest
from api_unittest_excel.common.db_handler import DBHandler, db
from api_unittest_excel.common.excel_handler import excel
from api_unittest_excel.common.logger_handler import logger
from api_unittest_excel.common.request_handler import RequestHandler
from api_unittest_excel.common.yaml_handler import yaml_data
from api_unittest_excel.config.setting import config
from api_unittest_excel.lib.ddt import ddt, data
from api_unittest_excel.middlerware.helper import save_token, Context
@ddt
class TestRecharge(unittest.TestCase):
def setUp(self) -> None:
self.req = RequestHandler()
save_token()
def tearDown(self) -> None:
self.req.close()
db.close()
test_datas = excel.read_excel("invest")
@data(*test_datas)
def test_recharge(self, test_data):
token = Context.token
member_id = Context.member_id
# 充值之前的余额
sql = "select * from member where id = %s;"
member = db.query(sql, args=[member_id, ])
before_money = member["leave_amount"]
if "#member_id#" in test_data["json_data"]:
test_data["json_data"] = test_data["json_data"].replace("#member_id#", member_id)
# 错误的member_id
if "#wrong_member_id#" in test_data["json_data"]:
test_data["json_data"] = test_data["json_data"].replace("#wrong_member_id#", member_id + 2)
headers = json.loads(test_data["headers"])
headers["Authorization"] = token
# 得到实际结果
res = self.req.visit(method=test_data.get("method"),
url=config.host + test_data.get("url"),
headers=json.loads(test_data.get("headers")),
json=json.loads(test_data.get("json_data")))
try:
self.assertEqual(res["code"], test_data.get("expected_result"))
logger.info("用例断言成功")
except AssertionError as e:
logger.error("用例断言失败:{}".format(e))
# 测试数据写回
excel.write_excel(config.data_path, "invest", test_data["case_id"] + 1, 9, "fail")
# 手动抛出异常 让用例执行失败
raise e
if res["code"] == 0:
# 充值金额
money = json.loads(test_data["json_data"])["amount"]
# 充值之后的余额
sql = "select * from member where id = %s;"
member = db.query(sql, args=[member_id, ])
after_money = member["leave_amount"]
self.assertEqual(before_money - money, after_money)
test_04_invest.py
import json
import unittest
from api_unittest_excel.common.db_handler import DBHandler
from api_unittest_excel.common.excel_handler import excel
from api_unittest_excel.common.logger_handler import logger
from api_unittest_excel.common.request_handler import RequestHandler
from api_unittest_excel.common.yaml_handler import yaml_data
from api_unittest_excel.config.setting import config
from api_unittest_excel.lib.ddt import ddt, data
from api_unittest_excel.middlerware.helper import save_token, Context, replace_labal
@ddt
class TestInvest(unittest.TestCase):
def setUp(self) -> None:
self.req = RequestHandler()
self.db = DBHandler(yaml_data["database"]["host"],
yaml_data["database"]["port"],
yaml_data["database"]["user"],
yaml_data["database"]["password"],
yaml_data["database"]["database"],
yaml_data["database"]["charset"])
# 添加项目 获取项目id
# 审核通过 是项目为可投资项目
# save_load_id
def tearDown(self) -> None:
self.req.close()
self.db.close()
test_datas = excel.read_excel("recharge")
@data(*test_datas)
def test_invest(self, test_data):
token = Context().token
member_id = Context().member_id
loan_id = Context().load_id
# 充值之前的余额
sql = "select * from member where id = %s;"
member = self.db.query(sql, args=[member_id, ])
before_money = member["leave_amount"]
test_data["json_data"] = replace_labal(test_data["json_data"])
headers = json.loads(test_data["headers"])
headers["Authorization"] = token
# 得到实际结果
res = self.req.visit(method=test_data.get("method"),
url=config.host + test_data.get("url"),
headers=json.loads(test_data.get("headers")),
json=json.loads(test_data.get("json_data")))
try:
self.assertEqual(res["code"], test_data.get("expected_result"))
logger.info("用例断言成功")
except AssertionError as e:
logger.error("用例断言失败:{}".format(e))
# 测试数据写回
excel.write_excel(config.data_path, "register", test_data["case_id"] + 1, 9, "fail")
# 手动抛出异常 让用例执行失败
raise e
if res["code"] == 0:
# 充值金额
money = json.loads(test_data["json_data"])["amount"]
# 充值之后的余额
sql = "select * from member where id = %s;"
member = self.db.query(sql, args=[member_id, ])
after_money = member["leave_amount"]
self.assertEqual(before_money + money, after_money)
# test_add.py
import json
import unittest
from api_unittest_excel.common.excel_handler import excel
from api_unittest_excel.common.request_handler import RequestHandler
from api_unittest_excel.config.setting import config
from api_unittest_excel.lib.ddt import ddt, data
from api_unittest_excel.middlerware.helper import replace_labal, Context
from api_unittest_excel.middlerware.qcd_db_handler import MyDBHandler
from api_unittest_excel.middlerware.qcd_logger import logger
@ddt
class TestAdd(unittest.TestCase):
# 读取数据
test_datas = excel.read_excel("add")
def setUp(self) -> None:
self.req = RequestHandler()
self.db = MyDBHandler()
def tearDown(self) -> None:
self.req.close()
self.db.close()
@data(*test_datas)
def test_add(self, test_data):
# 用的admin token
token = Context().token
headers = json.loads(test_data["headers"])
headers["Authorization"] = token
test_data["json_data"] = replace_labal(test_data["json_data"])
logger.info("*" * 80)
logger.info("执行测试用例:{}{}".format(test_data["case_id"], test_data["case_name"]))
# 访问接口,得到实际结果
res = self.req.visit(test_data["method"],
config.host + test_data["url"],
headers=headers,
json = json.loads(test_data["json_data"]))
try:
self.assertEqual(res["code"], test_data["excepted_result"])
# 测试结果写入Excel
excel.write_excel(config.data_path, "register", test_data["case_id"] + 1, 9, "pass")
# except AssertionError as e:
except Exception as e:
logger.error("用例断言失败:{}".format(e))
# 测试数据写回
excel.write_excel(config.data_path, "register", test_data["case_id"] + 1, 9, "fail")
# 手动抛出异常 让用例执行失败
raise e
import os
import time
import unittest
import datetime
# 项目路径
from TestRunner import HTMLTestRunner
from api_unittest_excel.config.setting import config
# dir_path = os.path.dirname(os.path.abspath(__file__))
# # 测试用例文件路径
# test_cases_path = os.path.join(dir_path, "test_cases")
# 自动找test开头的模块 已创建对象
suite = unittest.defaultTestLoader.discover(config.test_cases_path, "test_*.py")
# 生成时间挫
# fmt = str(int(time.time()))
# fmt = time.strftime('%Y-%m-%d%H:%M:%S',time.localtime(time.time()))
time_stamp = '{0:%Y%m%d%H%M%S}'.format(datetime.datetime.now())
report_name = "report{}.html".format(time_stamp)
report_name_path = os.path.join(config.reports_path, report_name)
# if not os.path.exists(report_name_path):
# os.mkfifo(report_name_path)
with open(report_name_path, "wb") as file: # 二进制形式
# 运行用例 生成HTML文本测试报告
runner = HTMLTestRunner(stream=file, verbosity=2, title="xiaoyu测试报告", description="用例")
runner.run(suite)
# 动态数据获取
# TODO 1/23
# 动态数据 例如 注册超过的手机号码 可以随机生成和数据库对比 维护代码
# 接口依赖
# 充值接口
# TODO middleware 中间件 helper。py
# jsonpath 解析json数据 提取
# $ 根节点 .子节点 ..所有节点
# $..token
token = jsonpath(data, "$..token")[0] # 在一个列表中 获取token
# getattr 使用
# TODO 1/24
# context 用类属性的方法存储临时变量
# 提交事务 数据同步的作用
self.conn.commit()
# Context 环境管理方式
# 投资模块
# 断言 接口返回 和 其他的校验 余额是否正确 看用例
# 添加项目和审核项目
# setattr
# Excel 表格 多个参数动态变换 用正则表达式
# 正则表达式 调用的字符串匹配技术
# python 匹配方式 re模块 正则表达式在线
# 匹配
import re
# 匹配的字符串 连续的
pattern = r"abc"
a = "abcdwalsdasldabcalsjkdacblaskdabcssssdasd"
# 从开始的位置匹配
res = re.match(pattern, a)
print(res)
# 全文匹配
re.search(pattern, a)
print(res)
# 全部匹配
re.findall(pattern, a)
print(res)
# 【abc 】[a-c] 匹配【】中任意其中一个字符
pattern = r"[abc]"
re.findall(pattern, a)
# r"." 匹配任意的一个字符串,除了\n
pattern = r"."
re.findall(pattern, a) # 返回的是列表
# \d 匹配任意一个数字字符等价于[0-9] data
pattern = r"\d"
# \D 匹配非数字字符
# \w 匹配数字、字母、下划线 等价于[A-Za-z0-9_]
# \W 匹配非数字、字母、下划线 等价于[^A-Za-z0-9_]
# r"\w{2}" 匹配两个连续\w {2}
# r"\w{2,}" 匹配至少两个连续\w
# 贪婪模式 python 默认 正则表达式
# {中打空格什么意思}
# r"\w{,2}" 匹配最多两个连续\w 0,1,2
# r"\w{2,4}" 匹配2-4次连续\w
# 匹配一个手机号码
r"1[35789]\d{9}"
# 邮箱号码
# * 匹配数字0次或任意次,通配符 常用 比如discover 中匹配test_*.py
r"\d*"
# + 匹配一次或任意次
r"\d+"
# 组合 r"\d." 匹配连续的数字和任意字符(除了\n)
# ?匹配为非贪婪模式
r"\d?" # 匹配0次或1次
# r"\d*?"
# ^ 开头
r"^\d" # 匹配开头
r"\d$" # 匹配结尾
r"\d*$" # 匹配结尾任意多次
# 匹配任意字符任意次 非贪婪模式
r"#.*?#" r"#.+?" # 匹配至少一次
r"#(.*?)#" # 组去掉#号
re.search()
# 替换操作
re.sub(pattern, repl=repl, string=strring, 1)
# todo 正则表达式替换字符串封装
def replace_labal(target):
re_pattern = r"#(.*?)#"
while re.findall(re_pattern, target):
key = re.search(re_pattern, target).group(1) # 获取一个#***# 变成***
target = re.sub(pattern, str(getattr(Context(), key)), target, 1) # getattr 获取动态变量类属性 替换字符串
return target
#jenkins 持续集成CI
# xml 百度搜索
# TODO 1/22
# __str__
# 动态数据 使脚本复用性强
# python操作数据库
import pymysql
# 建立连接实例化
conn = pymysql.connect(host="120.78.128.25", port=3306, user=future, password=password, charset="utf8",
database=futureloan,cursorclass=DictCursor) # 查询sql后返回的字典
# 游标 实例化游标
cursor = conn.cursor()
# 执行sql
cursor.execute("select * from member where mobile_phone=%s;",args=[mobile,]) #向sql传入参数 防止sql注入
# 获取查询sql后的数据 游标结果,光标
cursor.fetchone()
cursor.fetchall() # 默认返回元组包元祖
# 操作数据库 游标保持独立性
cursor.close()
conn.close()
# 关闭游标
# TODO 封装pymysql
import pymysql
from pymysql.cursors import DictCursor
class DBHandler:
def __init__(self, host, port, user, password, database,
charset=None, cursorclass=DictCursor, **kwargs):
"""初始化"""
self.conn = pymysql.connect(host=host,
port=port,
user=user,
password=password,
database=database,
charset=charset,
cursorclass=cursorclass,
**kwargs) # 返回字典数据
self.cursor = self.conn.cursor()
def query(self, sql, args=None, one=True):
self.cursor.execute(sql, args=args)
if one:
return self.cursor.fetchone()
else:
return self.cursor.fetchall()
def close(self):
self.cursor.close()
self.conn.close()
# json和字典的转换
json.loads() # json加载为python字典 json用双引号 null Nonez转换
json.dumps() # 字典转换为json
# json.decoder.JSONDecodeError: Extra data: line 1 column 24 (char 23)
# 在Excel表格中json数据没加{}
# 整体架构和框架
#动态数据处理
# 测试用例关联
# 正则表达式
# 数据库状态查看
# 框架的分层
# run_test.py:作用:代码人口,收集测试用例,生成测试报告
# test_cases 测试逻辑 各个模块的测试用例方法:便于管理
# 数据管理层 test_data Excel 表格管理
# 业务逻辑层 通用的 common
# 配置文件 (config 一些常量 例如地址什么的)
# 输出 reports 测试报告 日志文件
# python 配置文件 一般是程序动态获取的
# yaml 配置文件 一般是常量 写死的
# INI 老配置文件
# json.decoder.JSONDecodeError: Extra data: line 1 column 24 (char 23)
# JSON解码错误:额外的的数据 json.loads(json数据)
# 在Excel表格中json数据没加{}
# TODO 1/17/2021
# TODO 修改ddt源码
if isinstance(v, dict): # 用于函数封装修改,复制修改文件 类和对象是用继承重写就可以修改
test_name = mk_test_name(name, v["case_name"], i)
# log日志 记录信息便于定位问题 logging
# 日志级别
# 日志收集器 级别 处理器handler 处理器级别设置 设置日志格式format 添加日志处理器
logger = logging.getLogger("xiaoyu") # 笔记本
# 设置级别
logger.setLevel("DEBUG")
# 设置日志格式format
fmt = logging.Formatter("日志格式")
# 放到file文件中处理器handler 笔写在文件里
file_handler = logging.FileHandler("file.txt")
file_handler.setLevel("DEBUG")
file_handler.setFormatter(fmt)
# 添加handler
logger.addHandler(file_handler)
logger.info('hello')
# TODO logger 封装 继承重写logger
from logging import Logger, Formatter, FileHandler, StreamHandler
class LoggerHandler(Logger):
def __init__(self, name="root", level="DEBUG", fmt='%(asctime)s %(filename)s : %(levelname)s %(message)s',
file=None):
super().__init__(name)
self.setLevel(level)
fmt = Formatter(fmt)
if file:
file_handler = FileHandler(file)
file_handler.setLevel(level)
file_handler.setFormatter(fmt)
# 添加handler处理器
self.addHandler(file_handler)
stream_handler = StreamHandler()
stream_handler.setLevel(level)
stream_handler.setFormatter(fmt)
self.addHandler(stream_handler)
logger = LoggerHandler(file="log.txt")
# 配置文件
# 放入常量
# python模块
class Config:
level = "DEBUG"
# yaml文件 语法 pip install pyyaml
import yaml
# 读取文件
with open("w.yaml", encoding="utf-8") as f:
data = yaml.load(f.read(), Loader=yaml.FullLoader) # 文件流
# ini 文件 conf文件
config = ConfigParser()
# 读取文件
config.read("w.ini",encoding="utf-8")
a = config.get("tea","name") # section option
# sys模块
sys.platform
# TODO 1/18
# config 封装 读取ini
from configparser import ConfigParser
class ConfigHandler(ConfigParser):
def __init__(self, file, encoding="utf-8"):
super().__init__()
self.read(file, encoding=encoding)
if __name__ == '__main__':
config = ConfigHandler(file)
config.get(
section=section,
option=option
)
# 修改文件
config["teacher"]["option"] = "xiaoyu"
with open("f.ini",mode="a",encoding="utf-8") as f:
config.write(f)
# IO文件处理
open()
# 文件流 stream
open().read())
# 写入yaml文件
with open("file.yaml",mode="w",encoding="utf-8") as f:
yaml.dump(data,stream=f,allow_unicode = True)
# 什么样的功能或项目适合做自动化测试
# 测试流程
# TODO 2021/1/16
# 读取excel表格 读之前要关闭文件
wb = openpyxl.load_workbook(r"文件路径")
wb._sheets # 私有属性 sheet对象
# 创建sheet 激活
wb.active
wb.sheetnames # 列表存储sheet名字
sheet = wb.worksheets[0] # sheet 对象
# 通过名字获取sheet 对象
sheet1 = wb["sheetname"]
# 获取单元格对象
cell = sheet.cell(1, 2)
# 获取单元格值
cell.value
# 获取行、列
sheet[1]
sheet["A"]
# 获取多行 sheet[1:3] 1-3 行
# 获取所有数据
data = list(sheet.rows)
for row in data:
for cell in row:
print(cell.value)
# 保存Excel
wb.save(r"文件名称")
# 关闭
wb.close()
# TODO openpyxl excel 封装
from openpyxl import load_workbook
from openpyxl.worksheet.worksheet import Worksheet
class ExcelHandler:
"""操作Excel表格"""
def __init__(self, file):
"""初始化文件名"""
self.file = file
def open_sheet(self, sheet_name) -> Worksheet: # 函数的返回时worksheet 函数注解
"""打开表单"""
wb = load_workbook(self.file)
sheet = wb[sheet_name]
wb.close()
return sheet
def read_hander(self, sheet_name):
"""读取表头数据"""
sheet = self.open_sheet(sheet_name)
hander_data = sheet[1]
hander_list_data = []
for cell in hander_data:
hander_list_data.append(cell.value)
return hander_list_data
def read_excel(self, sheet_name):
"""读取所有的数据"""
sheet = self.open_sheet(sheet_name)
rows = list(sheet.rows)
data = []
for row in rows[1:]:
row_data = []
for cell in row:
row_data.append(cell.value)
dict_data = dict(zip(self.read_hander(sheet_name), row_data)) # zip用法 字典标题和数据一一对应 dict 字典转换
data.append(dict_data)
return data
@staticmethod
def write_excel(file, sheet_name, row, column, data):
"""写入Excel数据"""
wb = load_workbook(file)
sheet = wb[sheet_name]
sheet.cell(row, column).value = data
wb.save(file)
wb.close()
# TODO test_login.py 测试用例模块
# ddt -->data driver testing pip install ddt 是unittest的一个插件 数据驱动思想
# python/unittest/ddt 测试框架
@ddt.ddt # 作用
class Testlogin(unittest.TestCase):
def setUp(self) -> None:
pass
def tearDown(self) -> None:
pass
@ddt.data(*test_datas) # 传入列表数据[{},{}]
def test_login(self, test_data): # 传入字典数据
self.assertEqual(res, test_data["expected_result"])
# 加载指定模块 指定类
from HTMLTestRunner.HTMLTestRunner import HTMLTestRunner
suite = unittest.defaultTestLoader.loadTestsFromModule(test_login)
# 加载多个模块
suite_totle = unittest.makeSuite()
suite_totle.addTests(suite)
suite_totle.addTests(suite1)
# 生成HTML测试报告 HTMLTestRunnerNew库 可以放到site-pakage 公共库
# TODO 生成HTML报告
import os
import time
import unittest
import datetime
# 项目路径
from TestRunner import HTMLTestRunner
dir_path = os.path.dirname(os.path.abspath(__file__))
# 测试用例文件路径
test_cases_path = os.path.join(dir_path, "test_cases")
# 自动找test开头的模块 已创建对象
suite = unittest.defaultTestLoader.discover(test_cases_path, "test_*.py")
# 生成时间挫
fmt = str(int(time.time()))
# fmt = time.strftime('%Y-%m-%d%H:%M:%S',time.localtime(time.time()))
time_stamp = '{0:%Y%m%d%H%M%S}'.format(datetime.datetime.now())
report_name = "report{}.html".format(time_stamp)
report_name_path = os.path.join(dir_path, "reports", report_name)
# if not os.path.exists(report_name_path):
# os.mkfifo(report_name_path)
with open(report_name_path, "wb") as file: # 二进制形式
# 运行用例 生成HTML文本测试报告
runner = HTMLTestRunner(stream=file, verbosity=2, title="xiaoyu测试报告", description="用例")
runner.run(suite)
# 先执行setup,然后执行test用例,最后执行teardown
# Todo 执行测试用例 生成报告 run_test
import os
import unittest
# 查找、加载测试用例
dir_path = os.path.dirname(os.path.abspath(__file__))
test_cases_path = os.path.join(dir_path, "test_cases")
# 自动找test开头的模块 已创建对象
suite = unittest.defaultTestLoader.discover(test_cases_path, "test_*.py")
report_name = os.path.join(dir_path, "reports", "report1")
if not os.path.exists(report_name):
os.mkfifo(report_name)
with open(report_name, "w", encoding="utf8") as file:
# 运行用例 生成普通文本测试报告
runner = unittest.TextTestRunner(file, verbosity=2)
runner.run(suite)
# m是函数、方法 c是类 v是变量
# TODO 2021/1/13
# TODO requests封装
class RequestHandler:
def __init__(self):
self.session = requests.Session()
def visit(self, method, url, params, data, json, headers, **kwargs):
res = self.session.request(method, url, params=params, data=data, json=json, headers=headers, **kwargs)
try:
return res.json()
except ValueError:
print("not json")
# 如果想断言失败,要抛出异常
raise AssertionError
# 断言结果 。通过 pass F false 断言失败 E error 程序内部错误
# unittest.main()与右键unittest为什么不能同时运行
# 执行顺序 根据ASCII码 用数字来控制执行顺序
# 运行 用右键unittest 运行 在类名执行测试类,方法名执行单个测试用例 空行执行整个模块
# 命令行 运行unitest
# 断言方式
# self.assertEqual() 打印预期结果 assertTrue() 复制的表达方式 只有true
# assertIn assertRegex 正则表达式匹配
# if True
# 响应行
# 响应版本号
# 状态码 200 201 304 404 500
# 响应头 响应的数据格式 content-type
# 响应体 返回的数据
# cookie session和token的区别
# 输入url后的过程
# 三次握手四次挥手
# TODO 2021/1/12
# requests 库的使用
# get
import requests
# querystring 查询字符串的形式 放在url中
headers = {"token": "sada"}
params = {"token": "sada"}
res = requests.get(url, params=params, headers=headers)
# post 默认表单形式
from_data = {"asd": "Sdad"}
json_data = {"sda": "dsfw"}
requests.post(url=url, data=from_data, json=json_data, headers=headers)
# response 对象
res.text # 字符串
# 二进制形式 图片、视频
res.content
# json 字典数据类型
res.json()
# 注册接口url
url = "http://120.78.128.25:8766/futureloan/member/register"
headers = {"X-Lemonban-Media-Type": "Lemonban.v1"}
json_data = {"mobile_phone": "18773181811", "pwd": "1234567"}
res = requests.post(url=url, json=json_data, headers=headers)
print(res.json())
# 登录接口
url = "http://120.78.128.25:8766/futureloan/member/login"
headers = {"X-Lemonban-Media-Type": "Lemonban.v2"}
# token 放在哪里看接口文档
# cookie 请求头 http协议
res = requests.post(url, json=json_data, headers=headers) # 请求头自动带传递数据类型
print(res.json())
# 获取登录token 用来使用其他接口
# 充值接口
url = "http://120.78.128.25:8766/futureloan/member/recharge"
token = res.json()["data"]["token_info"]["token"]
id = res.json()["data"]["id"]
headers = {"X-Lemonban-Media-Type": "Lemonban.v2", "Authorization": "Bearer{}".format(token)}
data = {
"member_id": id,
"amount": 100
}
res = requests.post(url,json=data,headers = headers)
print(res.json())
# cookin 用session 会话会自动保存cookin
requests.session()
# requests封装
# 单元测试框架unittest 测试visit函数 类
# 单元测试框架和接口测试有什么关系 测试访问接口函数
# 模块 test开头 类 Test开头 函数 test开头
# 运行方式unittest与pytest切换
# 断言预期结果
# split 分割字符串为列表
a = "a,s,d"
a.split(",")
# 三目运算符
# time()
# isinstance
isinstance(a, str)
# [[],[],[]]转换为[{},{},{}]
cases = [[], [], []]
def transform_1():
title = cases[0]
new_case = []
for case in cases[1:]:
dict_data = {}
for i, data in enumerate(case):
dict_data[title[i]] = data
new_case.append(dict_data)
return new_case
# zip 用法 [1,2],[3,4]->[(1,3),(2,4)]
def transform_zip():
title = cases[0]
new_case = []
for case in cases[1:]:
dict_data = dict(zip(title, case))
new_case.append(dict_data)
return new_case
# random 内置摸快 time 直接模块名
# 模块导入 form 包名.模块名 import 类名,函数名,变量名 as r 重命名
# 模块的搜索顺序
# 打印这个模块的路径
print(__file__)
# __name__ 运行当前模块名 为__main__
if __name__ == "__main__":
# python内置模块在lib\site-packages
# os模块
import os
# pwd 显示当前文件夹路径
os.getcwd()
# 获取绝对路径
os.path.abspath(__file__)
# 获取文件夹路径
a = os.path.dirname(os.path.abspath(__file__))
# 路径拼接
b = os.path.join(a, "yu.txt")
# 创建文件夹
os.mkdir(a)
os.path.isdir(a)
os.path.isfile(a)
# 路径是否存在
os.path.exists(a)
"""
os,open 的作用
自动生成报告
打开、读取文件、关闭
"""
# 编码string-->byte 解码byte-->string encoding
# mode模式: r ,w 写,a 追加 b binary ,二进制 图片、视频 rb,wb t 文字模式 + 又读又写
with open(b, mode="r", encoding="utf-8") as file:
file.read()
# 读取一行
file.readline()
# 读取所有行
file.readlines()
file.close()
# 读取机制:根据光标移动 seek()
# 异常; 常见的异常类型
# IOError:输入输出异常
try:
pass
except IndexError as e: # 捕获错误类型给e
raise IndexError # 抛出异常 程序错误 控制异常
except TypeError as e:
pass
finally:
# 都会执行的语句
pass
# 类
class RenLei():
pass
class XiaoYu(object, RenLei): # 大驼峰式命名 继承 多重继承
xiaoyu = "yu" # 类属性
def __init__(self, name): # 初始化函数、初始化方法 只能返回None
self.name = name # 实例属性 self:表示实例自己,在函数里面使用
def hello(self): # 实例方法
return self.name
@staticmethod
def chen(): # 静态方法 方便管理
pass
@classmethod # 构造init
def make_instance(cls, name): # 类方法 cls = XiaoYu
return cls(name)
def __repr__(self): # 返回对象打印出来的效果,名字固定用法
return self.name
print(XiaoYu.xiaoyu) # 类属性的获取
chen = XiaoYu("xiaoyu") # 对象、实例、东西 object 具体的事物 实际调用init函数
print(chen.name) # 获取实例属性
chen.hello() # 获取实例方法
XiaoYu.chen() # 静态方法的调用
XiaoYu.make_instance() # 类方法的调用
# 格式化
f"sss{a}sdas"
# join() 用于将序列中的元素以指定的字符连接生成一个新的字符串。
str = "-"
seq = ("a", "b", "c") # 字符串序列
str.join(seq)
# 以上实例输出结果如下:
# a-b-c
# 类里面的函数就叫方法
# 静态方法
# 类的继承 子类可以使用父类的方法、属性
# 多重继承 继承多个父类
# 菱形问题 C3算法 广度优先 A\B(A)\C(A)\D(B,C)
# 深度优先 A\B(A)\C\D(B,C)
# 查找顺序
print(XiaoYu.__mro__)
# 超继承 更新父类方法
super().call() # 调用父类的方法
super().__init__() #
# debug 调试 断点调试
# getattr 获取类\对象的属性值
getattr(XiaoYu, "chenyu", "xiaoyu") # 类/对象 属性 属性默认值(没有属性值的时候)
# 设置属性 类\对象的属性值
setattr(XiaoYu, "sad", "sda")
# 接口基础
# 什么是接口 web api 是api中的一种
from flask import Flask
app = Flask(__name__)
@app.route("/login") # 函数和地址绑在了一起
def login():
return "登录成功"
if __name__ == '__main__':
app.run()
# 什么是网络请求 http websocket webservice
# http 请求协议
# 请求行 :URL 域名 DNS解析 IP地址 请求方法get head post put delect get和post区别
# body参数方式 content-type
# 请求头 user-agent 用户代理 content-type:请求数据格式 cookie 让无状态变有状态
# 请求体 请求数据
# 响应
# 响应行 响应头 响应体
# 列表 可变、有序类型 1/9/2021
# 索引
# 切边
# 长度 len()
# 添加新的元素
import random
a = ["ren"]
a.append("asd")
# 添加多个
a.extend(["asd", "sada", "sad"])
# 指定位置添加
a.insert(0, "sadsa")
# 删除指定的值
a.remove("sad")
# 删除指定的索引,弹出
a.pop(0)
# 删除末尾的值
a.pop()
# 删除所有的元素
a.clear()
# 不用del 从内存当中直接删除
# del a
# 修改
a[0] = "sada"
# index 返回索引
a.index("asd")
# count
a.count("Sad")
# 逆序
a.reverse()
# 逆序2 重新创建变量
b = a[::-1]
# 排序 数字排序
a.sort()
# 倒排
a.sort(reverse=True)
# 字典(可变,无序),有标记 key:value key 一般用字符串 不可变类型 唯一 分散存储
info = {"name": "xiaoyu", "age": 11}
# 获取某个元素
name = info["name"]
# 获取长度
len(info)
# 元素修改和添加
info["name"] = "sadas"
# 随机删除
info.popitem()
# 删除指定key
info.pop("name")
# 清除
info.clear()
# 获取[(key,value),(key,value)]
info.items()
# 获取key
info.keys()
# 获取values
info.values()
# 元组 tuple 不可变 有序
c = ("sada", "sda", "sada")
# 索引
# 切边
# a = (1,) 元组 a=1,2 元组 a=(1) 非元祖
# 元祖解包
a, d = 3, 4
# 集合 无序
# 去重 list(set(list))
yu = {"sada", "Dsad", "Sdad"}
# 构造函数
dict()
# 算数运算
# 浮点数运算 用Decimal函数
# 赋值运算
# 比较运算
# 逻辑运算 and or not
# 成员运算 in not in
# 字典中in 是判断key值
# if ...elif..else:..
# python遇到冒号要缩进
# for循环 遍历
# 字典
dict_data = {"name": "xiaoyu", "age": "18"}
for i, v in dict_data.items(): # 元祖解包
print(i, v, end="/")
# while(重点) 死循环
i = 0
while True:
print("sad")
if i == 999:
print("循环结束")
break # 结束循环
i += 1
continue # 退出这一次循环,执行下一次循环
print("结束")
# 获取随机数 1-3
random.randint(1, 3)
# 获取最大值
max(a, b, c)
# 获取1-9 用于for循环
range(1, 9)
# 99乘法表
for i in range(1, 10):
for j in range(1, i + 1):
print("{}*{}={}".format(j, i, i * j), end=" ")
print()
# for 循环不要修改列表 可以新建列表复制出来 或外循环
list_data = ["1", "sad", "21"]
for we in list_data[:]:
list_data.remove(we)
# index+1
# 函数 存储一段可以重复执行的程序
# 遇到return 程序终止
# 函数参数 位置参数 关键字参数 默认参数 不定长参数、动态参数
def sums(a, b="a", *args, **kwargs): # **kwargs keyword-arguments接受多余的关键字参数
# args 是元祖 kwargs 是字典
pass
data = [1, 23, 4, 5]
sums(1, b="2", *data, **dict_data) # *data 解包脱衣服 **解包字典
# 执行代码先函数先注册、定义
# 函数内局部变量 全局变量
global list_data # 关键字 声明全局变量 会造成数据紊乱
# python内置函数 print() input() len() max()
# range() 是一个类
# enumerate() 枚举
for index, i in enumerate([1, 3, 4, 7]):
print(index, i)
# 查看内存地址 id()
# eval() 脱字符串的衣服
# zip() round()
# 模块 module 就是python文件
# 包 package python文件的文件夹 包含一个 __init__.py文件
# 查看源码方法 1、注释 2、函数名 3、参数 4、返回
name = """
sadas
sadadass
sad"""
# python 数据类型 int float bool str list tuplr dict set
# pycharm debug 调试 计算器使用
# 查看数据类型 type(file)
# 数据类型的转化 int(str) str() bool() dict() list()
# 字符串 不可变类型
# r的使用 转义字符变成正常字符 file = r"d:\hshs\shsj.txt"
# 字符串的操作
# 字符串拼接 + 重复多次 *
# 获取字符串的长度
# 数据索引 索引超出范围会报错 IndexError
# 字符串切片
# 格式化
"{}{}{}{}".format(c, a, a, v)
# 复制字符串 切片复制
# 字符串内置函数
str1 = "abc"
# 获取长度
len(str1)
# 转化成大写
str1.upper()
# 转化成小写
str1.upper().lower()
# 大小写转换
str1.upper().lower().swapcase()
# 查找
str1.find("a") # 经常使用
str1.index("q") # ValueError 会报错
# 替换 并重新给新的对象
str2 = str1.replace("a", "s", 1) #经常使用
# 统计
str1.count("a")
# 字符串拼接,正规方式的+
".".join([str1, str2]) # 用的比较多
# 字符串分割
str1.split("++") #经常使用
# 去掉左右两边的指定字符串
str1.strip(" a") #经常使用
# 去掉左边 右边
str1.lstrip("a").rstrip("c")
# 判断是否是正整数
str1.isdigit() #经常使用
# 变量 存储数据
# 字符串: 单引号 双引号 三引号:写多行字符串
断言
日志
import requests
url = 'http://127.0.0.1:5000/api/huace/userDelete'
# delete
res = requests.delete(url)
print(res.json())
# auto 鉴权接口
url = "http://127.0.0.1:5000/api/huace/auth"
session = requests.session()
session.auth = ("admin", "huace123456")
res = session.request("post", url)
print(res.json())
# 登录加密接口
# session会话管理 --token
url = 'http://127.0.0.1:5000/api/huace/login'
data = {
"username": "admin",
"password": "123456"
}
headers = {"Content-Type": 'application/json'} # 字典
# 创建session对象
session = requests.session()
res = session.request("post", url, json=data, headers=headers)
'''获取token值,将token值写入head
1:登录接口的响应结果中获取token节点的值
2:获取token节点的值新增到head参数中去
'''
get_token = res.json()['token']
# print("登录接口token节点的值:",get_token)
# session会话去更新headers中的内容值
session.headers.update({'token': get_token}) # {'token':''huacetest''}
# 查看请求头中是否有添加
print(session.headers)
'''用户详情信息的接口'''
url2 = 'http://127.0.0.1:5000/api/huace/userList'
res2 = session.get(url2)
print(res2.text)
# httprequests 封装
import requests
class HttpClientRequest():
def __init__(self):
# 创建会话
self.session = requests.session()
self.url_pre = 'http://127.0.0.1:5000/api/huace/'
def init_headers(self, head=None):
self.head = {"Content-Type": 'application/json'}
if head:
self.session.headers.update(head)
def SendRequest(self, method, url, **kwargs):
self.url = self.url_pre + url
# print(kwargs) #{'json': {'username': 'admin', 'password': '123456'}}
self.data = None
if 'json' in kwargs:
self.data = kwargs['json'] # {'username': 'admin', 'password': '123456'}
self.res = self.session.request(method=method, url=self.url, json=self.data)
self.response = self.res.json()
# 提取响应结果中的值 --token:huacetest
if 'tiqu' in kwargs: # {'json': {'username': 'admin', 'password': '123456'}, 'tiqu': 'token'}
self.val = kwargs['tiqu'] # 'token'
self.get_token = self.response[self.val]
# token:huacetest追加到请求头中去
# self.session.headers.update({self.val:self.get_token})
self.init_headers({self.val: self.get_token})
return self.response
import json
import requests
from jsonpath import jsonpath
url = "http://49.233.108.117:3000/api/v1"
data = {
"page": "1",
"tab": "share",
"limit": 3,
"mdrender": True
}
res = requests.get(url + "/topics", params=data)
print(res.json()) # 字典
# 对结果进行格式化处理
res_json = json.dumps(res.json(), indent=4, ensure_ascii=False) # json格式
print(res_json)
# 接口关联
# 新建主题
json_data = {
"accesstoken": "a4b7e66f-12d7-4543-95e7-e6e7ad821fbf",
"title": "小小小小adsad",
"tab": "ask",
"content": "chenchen"
}
headers = {"Content-Type": "application/json"}
res = requests.post(url + "/topics", json=json_data, headers=headers)
print(res.json())
# 获取topic_id
topic_id = jsonpath(res.json(), "$.topic_id")[0]
print(topic_id)
# 编辑主题
json_data1 = {
"accesstoken": "1c2922bb-5789-4abd-9f46-639a8a57ca00",
"title": "小小小小adsad11111111",
"topic_id": topic_id,
"tab": "ask",
"content": "chenchen"
}
res = requests.post(url + "/topics/update", json=json_data1, headers=headers)
print(res.json())
url1 = "http://www.testingedu.com.cn:8000/index.php?m=Home&c=User&a=do_login"
headers1 = {"Content-Type": "application/x-www-form-urlencoded"}
data = {
"username": 13800138006,
"password": 123456,
"verify_code": 1111
}
# cookie关联 用session
session = requests.session()
res = session.post(url1, data=data, headers=headers1)
print(res.json())
res = session.get("http://www.testingedu.com.cn:8000/Home/Order/order_list.html")
print(res.text)
# 文件上传
url = "http://www.testingedu.com.cn:8000/index.php/home/Uploadify/imageUp/savepath/head_pic/pictitle/banner/dir/images.html"
file = {"file": ("baidu.png", open(r"D:\文档\pythonProject\api_auto\test\baidu.png", "rb"), "image/png")}
data = {
"name": "baidu.png"
}
res = requests.post(url, data=data, files=file)
print(res.json())
# token关联
url = "http://127.0.0.1:5000/api/huace/login"
data = {
"username": "admin",
"password": "123456"
}
headers = {"Content-Type": 'application/json'}
# 登录接口
res = requests.post(url, json=data, headers=headers)
token = jsonpath(res.json(), "$.token")[0]
headers["token"] = token
# 用户详细信息接口
url = "http://127.0.0.1:5000/api/huace/userList"
res = requests.get(url, headers=headers)
print(res.text)
import requests
url = "https://www.baidu.com/"
res = requests.get(url)
print(res.status_code)
print(res.text)
print(res.encoding)
# 修改响应对象的编码格式
res.encoding = "utf-8"
print(res.text)
# get请求带参数
data = {"id": 1001}
data1 = {"id": "1001,1002"}
data2 = {"id": 1001, "kw": "长沙"}
# res = requests.get(url, params=data2)
res = requests.request("get", url, params=data2)
print(res.url)
print(res.text)
import json
import requests
url = "http://api.test.zhulogic.com/designer_api/account/login_quick"
headers = {"Content-Type": "application/json"}
json_data = {
"phone": 15100001111,
"code": 1234,
"unionid": "",
"messageType": 3,
"channel": "zhulogic"
}
print(json.dumps(json_data)) # 将字典转换为json格式
res = requests.post(url, json=json_data, headers=headers)
print(res.text, type(res.text)) # 返回字符串
print(res.json(), type(res.json())) # 返回字典
# content 主要用于图片、视频格式
url1 = "https://www.baidu.com/img/flexible/logo/pc/result.png"
res = requests.get(url1)
print(res.content)
with open("baidu.png", mode="wb", ) as file:
file.write(res.content)