联合阿里在职测开工程师耗时一个星期写的 【接口测试+自动化接口接口测试详解]_fiddle定时

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新软件测试全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注软件测试)
img

正文

3:swagger工具能导出接口文档的

4:前端页面:

5:后端:

6:前端和后端的数据交互(接口)通过接口

7:接口的概念

8:常用的接口方式(协议)

9:什么是接口测试:

10:接口测试目标

11:接口自动化测试概叙(怎么做)  (接口跑的是协议层。ui定位的是元素)

12:http协议概述

13:fiddler(抓包工具的使用)

14:接口工具对比:(掌握fidder)

15:websocket: (和http一样是传输协议)

16:cookie详解

17:sessionid:翻译为会话id

18:cookie和sessionid合作流程(常见的方式)

19:tooken:令牌

20:json格式详解

22:python自动化过程中配置文件格式:

23:简单的接口测试源代码

24:requests.post方法:data和json参数区别(重点)

25:requests库请对应的方法和相应的参数:(request库最基本的四种方法)

26:requests库响应消息体四种格式:(打印的信息)

27:request库的使用,查看请求信息和响应信息,设置编码等

28:get可以post来提交,get参数写到/后面去,

29:cookie值的获取和传递操作实战

三:通过cookie对象获取sessionid值

30:一般头部header和cookie等都封装成字典,灵活,字符串不一定能识别(请求头和响应头一般都是字典)

31:token值得获取和传递操作实战

二:加密的token  token加密+上传文件接口

32:测试用例的设计

33:自动化测试用例设计:代码好处理,接口来说,需要

35:接口用例

36:接口项目代码大概框架如下

37:接口测试代码实战

38:接口api编写心得:

39:get请求两种写法

40:面试自动化做到什么程度

41:http和web socket

42:yaml    专门用来做配置文件的

43:项目总结

44:allure报告优化

45:接口自动化代码写道后期需要加的三个步骤

46:mock技术的简介  mock(模拟技术)

47:支付电商+异步接口(结合mock技术)

48:同步接口和异步接口(教官系统里面的接口都是同步接口)

重点:学习资料学习当然离不开资料,这里当然也给你们准备了600G的学习资料

项目实战

大型电商项目

全套软件测试自动化测试教学视频

300G教程资料下载【视频教程+PPT+项目源码】

全套软件测试自动化测试大厂面经

python自动化测试++全套模板+性能测试


b站top1全栈自动化测试教程,python全栈自动化测试

前言:

兄弟们如果本文让你感觉能学到东西就麻烦各位动动发财的手来个一键三连,听说点赞,收藏,评论,关注的兄弟已经跳槽涨薪发财了哦。

【文章末尾给大家留下了大量的福利】

1:json模块的使用

字典是一种存储类型,json是一种格式(完全不同)   json.loads()函数是将json字符串转化为字典(dict)   json.dumps()函数是将字典转化为json字符串   json.dump()和json.load()主要用来读写json文件函数

2:接口自动化测试概叙

什么是接口测试:
前后端不分离:淘宝网站(响应的数据在页面,访问响应的数据是html的)返回的是一整个html(做接口难,需要解析数据,因为返回的是整个html代码)
前后端分离,前端和后端通过api(接口交互),返回的只是数据本身(App可能并不需要后端返回一个HTML网页)
(市面上主流项目前后端分离走json格式的)
发请求以json数据格式返回的,通过api接口协议前后端进行交互的

3:swagger工具能导出接口文档的

4:前端页面:

安卓或者ios app,网页统层为前端展示(数据展示和用户的交互),
前端框架:html,js,css,vue(展示漂亮),nodejs

5:后端:

后台数据处理,校验,下订单等等业务处理(c语言,c++,java(大型都走java),go,python)

6:前端和后端的数据交互(接口)通过接口

有些问题前端可能屏蔽,但是后端没有做限制,校验都没有做的,绕过前端,抓包发请求的方式攻破后端,项目可能出现问题

7:接口的概念

**接口是应用程序之间的相互调用
接口是实体或者软件提供给外界的一种服务
软件接口:api,微信提现调用银联的接口实现数据交互

一种是内部接口:
1:方法与方法之间的交互 2:模块与模块之间的交互
一种调用对外部包装的接口
web接口:http,https,webserver(目前大多做web接口)
应用程序接口:soket接口  走的tcp/ip协议的
数据库接口:**

8:常用的接口方式(协议)

1:webservice :走soap协议通过http传输,请求报文和返回报文都是xml格式的,xml格式(soapui抓包)老项目(政府和银行)
还要解析数据,麻烦,而且速度可能有降低,通信比较严格
2:http协议:超文本传输协议(百分之70-80都走的http协议)  get post delete put四种主要的请求方式
3:https协议:并非是应用层的一种新协议,只是http通信接口部分用ssl和tls协议代替而已

9:什么是接口测试:

项目需需求
案例:一个登录接口
场景:产品上规定用户名6-10个字符串下划线
测试人员在前端做了校验,通过
后端开发人员没有做校验
风险:直接抓包取篡改你的接口,然后绕过验证,通过sql注入直接随意登录
危害:公司损失
接口测试是市场的主流需求

10:接口测试目标

可以发现客户端没有发现的bug,(隐藏的bug)(提交订单,前端屏蔽了后端没有,可以随便乱填的)
及早爆出风险(保证质量正常上线)
接口稳定了,前端随便改
最重要加内存系统安全性,稳定性

11:接口自动化测试概叙(怎么做)  (接口跑的是协议层。ui定位的是元素)

1:项目业务(了解项目业务)
2:接口文档(api文档)
3:接口用例
4:自动化脚本(根据接口文档和接口用例)
5:pytest框架  重点
6:调试执行   重点
7:allure报告
8:结果分析
9:持续集成

12:http协议概述

一:http响应报文包含四部分:如上图
1:请求行:主要包含信息:POST (请求方法)/search(请求url) HTTP/1.1 (http协议及版本)

二:http请求报文包含四部分:如上图
1:请求行:    POST (请求方法)/search(请求url) HTTP/1.1 (http协议及版本)
2:请求头     请求头,项目可能有防爬操作,python等语言请求被屏蔽,所以可能需要加头伪装
3:请求空行
4:请求体(请求正文)

三:通过谷歌浏览器F12分析http报文(如上图)  
Host : 主机ip地址或域名
User-Agent :用户客户端,比如说操作系统或者浏览器的版本信息,
(客户端相关信息,如操作系统,浏览器信息)可能拿到User-Agent去做兼容的,
服务器拿到User-Agent服务器生成兼容客户端的文本
浏览器需要服务器发回兼容我浏览器的版本,服务器拿到信息作一个判断,
Accept: 接收的意思,指定客户端接收信息的类型,(我想接收的是一个图片还是html还是json还是一个xml文档告诉服务器,服务器对应返回)
如imag/jpg/,text/html,有时候我们一个接口给很多系统公司去用,这个系统可能想要的xml就告诉服务器你给我传递xml数据
有些公司想要的是json数据(主流都是json格式)告诉服务器你给我json
这里可以在请求头域设置Accept告诉服务器我需要的内容的格式
Accept-Charset :客户端接收的字符集,客户端接收字符集告诉服务器:如gb2312,iso-8859-1,utf-8
Accept-Encoding: 可接受内容的编码,如:gzip压缩格式, deflate
(为了方便进行内容的传输,节省传输时间,提供传输效率,可能进行一些编码和压缩,节省传输流量)
Accept-Language:接受的语言,如Accept-Language: zh-CN,zh;q=0.9 ,做国际化可以识别这里,中文环境给我中文数据
Authorization:客户端提供给服务端,进行权限认证的信息(进行权限认证的,请求的话有个鉴权的这样的功能,
不是说想什么服务器就发给什么,有可能需要进行一些基本的认证,认证信息可以放在这里)
Cookie:携带的cookie信息 非常重要的字段(我登录成功之后就会在本地保存一些cookie,保存一些文本,
下一个请求的时候直接把cookie发给服务器,告诉服务器我已经登录了,有一个证明在这里cookie携带回去)做校验的
Referer:当前文档的URL,即从哪个链接过来的(我这个请求到底从那个连接过来的)
比如:Referer: http://test.lemonban.com/ningmengban/app/login/login.html登录的这个连接
是从login/login.html从我们这个网站的首页来的,防止盗链,把这个链接盗取了用到自己网站去,
或者说可以统计我这样的一个请求到底来自于那个页面,
Content - Type:请求体内容类型,请求构建以什么形式传递参数的,如:x-www-form-urlencoded表单形式
(往服务器提交内容,告诉服务器我提交的内容是什么格式的,表单,json格式,xml格式)
Content - Length:数据长度 ,(提交内容的数据长度有多少个字节告诉服务器,防止进行请求的篡改)
Cache-Control:缓存机制,如Cache-Control:no-cache :别给我缓存 ,有些请求会缓存到本地的,
Pragma:防止页面被缓存,和Cache-Control:no-cache作用一样(1.0中间的,为了兼容和1.0)

四:cookie 
第一次访问网站,如果网站有cookie机制,访问第一个请求的时候返回set-cookie
set-cookie设置cookie值,通过响应数据的头,响应头里面通过set-cookie把值给客户端,获取cookie里面对应的值

13:fiddler(抓包工具的使用)

**fidder的使用技巧(能抓包,查看抓包数据):
能抓取https要设置证书:(免费的,开源的,能抓很多对应消息,app的也可以)
fidder是一款免费,灵活,操作简单,功能强大的http代理工具,是目前最常用的http抓包工具之一
可以抓取所有的http/https包,过滤会话,分析请求详细内容,伪造客户端请求,篡改服务器响应,重定向,网络限速,断点调试等功能
fiddler的工作原理:
正向代理(正向代理服务器,通过浏览器发送请求以前是直接发给服务器,fidder转发(代理服务器),
浏览器请求发给fidder代理服务器,fidder代理服务器转发给服务器,
服务器数据转发给fidder代理服务器,代理服务器发给浏览器

正向代理:
转发浏览器的请求和响应,抓包工具 对客户端透明
反向代理:
nginx——负载均衡的——性能  一个服务器
tomcat db(现在用户级别很大,一个tomcat搞不定,需要帮手,三个tomcat分担流量(怎么协调加nginx–负载均衡))
浏览器发请求过来,不知道请求发给谁,请求量很大,通过nginx把请求分发到各个tomcat里面去,避免的一个tomcat承受不住**

14:接口工具对比:(掌握fidder)

fidder       免费的http查看工具,系统代理,工作在应用层          独立运行(是)      支持移动设备(是)     是否收费(否)
wireshark      网络抓包,监听网卡,工作在网络接口层             独立运行(是)      支持移动设备(否)     是否收费(否)
httpwatch      集成到IE,chrome中的网页数据分析工具             独立运行(否)      支持移动设备(否)     是否收费(基础版/专业版)
charles       http代理,http监控,http反代理,查看http通讯查看信息工具   独立运行(是)      支持移动设备(是)     是否收费(是)
ByrpSuite      http代理,用于工具web应用程序的集成平台,         独立运行(是)      支持移动设备(是)      是否收费(是)
包含了很多工具,抓包,漏扫,爆破,黑客必备工具之
wireshark–底层,网络层–用这个

15:websocket: (和http一样是传输协议)

websocket传输协议实时性强
使用场景: 聊天系统 股权交易 直播–部分业务

16:cookie详解

**服务器发给客户端:请求的响应数据  响应头:Set-cookies   本地存放cookies攻击  
cookie缓存身份的概念:a请求发给服务器,a第一个请求没有带cookie,比如登录操作一开始请求不带cookie的,服务器响应把cookie放到响应头:set-cookie
浏览器拿到set-cookie会自己进行处理,然后第二个请求发出去,就会带上cookie进行校验
浏览器访问里面自己传递,但是接口请求是不会自己带cookie的,需要提取cookie
a响应头里面把cookie拿出来,放到一个变量,下一个b请求把cookie放到请求头
不同用户的cookie不一样的,不然会冲突的
cookie里面包含很多内容:但是最常见的是sessionid(会话id)
简单项目里面有cookie就差不多了,但是有的项目里面cookie里面带token-复杂点
cookie是分站点的,站点和站点之间的cookie是相互独立的
浏览器的cookie是保存在浏览器的某个位置
服务器端可以通过:响应头中的set-cookie参数,对客户端的cookie进行管理
浏览器的每次请求,都会把该站点的cookie发送给服务器进行匹配校验

实现登录:cookie+session配合使用
cookie不是只有登录,发送请求访问首页都有cookie。
cookie放在浏览器本地的(缓存里面),sessionid放在服务器的上
sessionid必须寄生在cookie里面,搭配cookie一起使用(sessionid值一般在cookie里面传过去的)**

**17:sessionid:**翻译为会话id

sessionid就是会话id 身份验证-存放到 服务器
session是一个对象,是服务器产生的,保存在服务器中,
session有自己的管理机制,包括session产生,销毁,超时等
session id是session对象的一个属性,是全局唯一的,永远不会重复

18:cookie和sessionid合作流程(常见的方式)

一:快速理解
用户登录成功服务器创建session,返回给客户端,客户端浏览器把session保存到它的cookie里
二:过程描述
登录成功服务器立马创建session,并通过(响应头)中的set-cookie属性把session返回给客户端
浏览器把响应头中的set-cookie内容保存起来,存在浏览器自己的cookie中
以后浏览器每次发送请求时,都会把该站点的全部cookie封装到请求头中,传递给服务器

19:tooken:令牌

**token:
令牌(令牌代表身份信息,身份标识----身份校验(数据库db校验,每发个请求就需要校验很麻烦,避免频繁访问数据库,token就表示身份))
cookie和token(cookie里面最关键的就是session id值)
cookie里面有时候有sessionid和token值
一般为了减少对数据库的访问,校验,数据库的账号密码,
做个令牌:token需要身份校验,账号密码校验,获取token值(返回token字符串))
token也是由服务器参数的,存在服务器的内存或者硬盘中
有一套产生规则,会涉及到加密算法

用token来实现登录
开发提供一个获取token接口,根据用户名+密码,获取一个token值–**返回一个token(字符串)
**token值服务器通过什么发给客户端  
通过响应头传给客户端      次要
通过响应消息体传给客户端      主要
通过cookie传递给客户端     很少

token使用场景:
服务器访问的同时,vip学员访问服务器,怎么保证vip学员的权限,普通用户也能访问,vip学员的访问的时候加一个验证,vip访问的同时加个验证,
访问网站同时校验身份,必须去vip官网去认证,通过vip账号密码拿到token值,去访问服务器时候时候token带上。
做二次校验,身份的校验,不是每个人都能访问这个网站,只有vip学员才可以,
访问之前vip学员拿vip账号去网站认证一下,获取到token值然后每次访问都带上这个token就可以了,
这个token可以在cookie里面,token放头headers里面和cookie里面都可以,这时候token值,cookie里面自行加token值,自行组装
额外增加某些校验参数,cookie里面可以加一些值,如token值,自行封装cookie  
认证的时候token身份二次校验,token放cookie里面和头里面都行(校验是不是vip学员)**

19:接口测试数据格式

一:接口自动化测试数据源(测试用例来源哪里)测试用例一般存放在哪里
1:excel测试用例
2:yaml格式用例
3:csv
二:自动化脚本中的数据类型:
1:表单格式:
[urlencoded格式]:又叫form格式,或者是x-www-form-urlencoded格式(type类型为表单形式,传数据表单形式)
表单格式是键值对组成的,键和值之间用=,多个值之间用& 如:name=zhengsan&age=16
2:json格式(str字符串:键值对类型的字符串) 
json有对象,数组两种结构,有字符串,数字,逻辑值,空值四种数据类型
用大括号{}表示对象,对象由属性组成的,属性由键值对组成的,键和值用冒号隔开,属性之间用逗号隔开,键必须用双引号引起来
用中括号[]表示数组,数组由单独的值组成
json灵活,json可以嵌套

20:json格式详解

对象:大括号表示,对象由属性组成,属性由键值对组成,键和值对之间用冒号隔开,属性之间用逗号隔开,另外键必须双引号
{“姓名”:“姚明”,“年龄”:38}
数组:用中括号表示
[“小明”,“小李”,“小百”]
[{“姓名”:“姚明”,“年龄”:38}]
嵌套:对象中可以再嵌套对象和数组
{“姓名”:“姚明”,“年龄”:38,“家禽”:[“小明”,“小李”,“小百”]}

21:了解教管系统:(在线管理系统)(前后端分离的) 什么框架,什么配置

项目描述:
教师管理系统是一款在线管理学生课程的一款软件
主要功能包括:
课程:包含了所有课程名和课程详情
老师:可以查看老师姓名,授课内容及授课班级
学生:包括班级号,班级包含课程,所属学生和班级学生进度
框架结构:
后端架构:cherrypy与django结合
diango:包含了所有的课程名和课程详情
cherrypy:使用cherrypy作为diango的development server
数据库:
sqlite
前端:
html,js,css

22:python自动化过程中配置文件格式**:**

配置文件格式:text(很少用),cnf,ini,yaml,excel(很少用),.py也可以

23:简单的接口测试源代码

**config.py            #配置文件
#time:2020/10/5
HOST=“http://127.0.0.1:80”         #等同localhost,但是建议写localhost,写url全称,项目换了环境只要改这个配置url就行
http://127.0.0.1:80/mgr/login/login.html
http://127.0.0.1:80/mgr/login/login.html
http: // localhost / mgr / login / login.html
账号和密码:auto / sdfsdfsdf

login.py

登录接口

import requests                      #请求库使用request
import json
from api接口自动化.teachTest.config import HOST
#user_headers={“User-Agent”:“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36”}

2:url路径–考虑可维护性(各个环境ip不同,不同环境测试,增加配置文件)

url =f"{HOST}/api/mgr/loginReq"

#3:头部,请求头—头都是字典类型的
header={“Content-Typ”:“application/x-www-form-urlencoded”}       #type类型

#4–请求体body,最常用都是字典写的(传表单形式可以字典来做)
payload={“username”:“auto”,“password”:“sdfsdfsdf”}
print(type(payload))

#5–发送请求
#def post(url, data=None, json=None, **kwargs):
‘’’
url:必填
data=None #请求体的参数—默认表单类型—写的参数默认转成表单传递给客户端—application/x-www-form-urlencoded(传表单就是传data)
传data就不用传type—因为传的时候默认header头格式是表单格式
json=None #请求体的参数—默认json格式—写的参数默认转成json传递—application/json (传json格式就传json)
**kwargs #关键字可变数量参数,传 变量=值(一般传递:头headers,cookies,proxy代理,ssl校验)
cookies,token–一般封装到头部。
‘’’
#reps=requests.post(url,data=payload,headers=header) #reps请求本身的对象

1:打印请求头 print(reps.request.headers)
2:打印请求体 print(reps.request.body) #username=auto&password=sdfsdfsdf
3:打印响应体 print(reps.text) #text这是查询str字符串类型,返回的类型:字符串

print(reps.json())                           #也能查看响应体的,但是响应体必须是json,不是json就报错了–(返回的类型:字典)
print(json.loads(reps.text)[“retcode”],type(reps.text)) #返回的类型:字符串,转字典json.loads()
4查看响应头 print(reps.headers)        #访问接口,接口返回一个Set-Cookie,Set-Cookie是客户服务器给客户端的,在headers里面体现出来
cookie值如果后面接口需要就需要带上去(标识)

5–用json传输入,
reps=requests.post(url,json=payload,headers=user_headers) #这样写传入的数据是json格式,但是接口文档要求传入表单,肯定不会成功,
reps.encoding=“unicode_escape” #编码设置,为了看到中文(设置响应的编码格式)
print(reps.text)     查看响应体
print(reps.request.headers)   查看请求头 输出:header={“Content-Typ”,“application/x-www-form-urlencoded”}

reps=requests.post(url,json=json.dumps(payload),headers=header) #指明头,传的格式是表单(这样还是不行,后期自己研究)
reps.encoding=“unicode_escape”

print(reps.request.headers)

print(reps.text)

#6:这样发请求fidder抓不到包(发给服务的没有经过fidder代理,fidder没有中转)
#如果需要fiddler抓包需要加个代理参数proxies:fiddler抓包需要请求经过他,—设置代理(设置代码发请求到fidder)
fiddler_porxies={
“http”:“http://127.0.0.1:8888”,
“https”:“http://127.0.0.1:8888”
} #写fiddler的端口号+fiddler的路径, 抓包两个协议,http和https(都走的http所以下面的不用改成https)
reps=requests.post(url,data=payload,headers=header,proxies=fiddler_porxies) #porxies=porxies加到代理专门参数
reps.encoding=“unicode_escape”
print(reps.request.headers)
print(reps.text)

lesson.py
#time:2020/10/6
import requests
import json
from api接口自动化.teachTest.config import HOST
lesson_url =f"{HOST}/api/mgr/sq_mgr/"
Host = ‘http://127.0.0.1:80’
api_url = f’{Host}/api/mgr/sq_mgr/’
header = {‘Content-Type’: ‘application/x-www-form-urlencoded’}#字典
payload={
“action”:“add_course”,
“data”:‘’‘{“name”:“初中化学”,
“desc”:“初中化学课程”,
“display_idx”:“4”
}’‘’
}
print(type(payload)) #根据接口文档data里面要是个json,现在这么写{“name”:“初中化学”, “desc”:“初中化学课程”,“display_idx”:“4”}是个字典
#加三引号’‘’{“name”:“初中化学”, “desc”:“初中化学课程”,“display_idx”:“4”}‘’',表达是个json
reps = requests.post(lesson_url,data=payload)
reps.encoding = ‘unicode_escape’
print(reps.request.headers)
print(reps.text)

‘’’

type–json

#参数 payload2
1- json参数—建议使用 reps = requests.post(api_url,json=payload2)
2- data参数 import json
reps = requests.post(api_url,data=json.dumps(payload2),headers = header)
使用data参数传递json,(接口本身接收的数据是json)data写json—头也要指定json格式,使用data默认是表单,所以要改成json,
json.dumps()用于将字典形式的数据转化为字符串
data=json格式----headers=json格式(两边都要转,麻烦)
‘’'**

24:requests.post方法:data和json参数区别(重点)

**一:params参数,
如果传入的是字典,自动编码为表单,针对的是get方法,
一般get请求需要—def get(url, params=None, **kwargs):----只有get请求传有params,其他请求没有
二:data参数。
如果传入是字典,自动编码为表单形式传递, 针对的是post/put方法
三:data参数,
如果传入的是字符串,按原格式直接发布出去 针对的是post/put方法
四:json参数,
如果传入的是字典,自动编码为json字符串 针对的是post/put方法
五:json格式,
如果传入的是字符串,按原格式基础上添加双引号发布出去 针对的是post/put方法
六:headers参数,
传递的是字典格式,针对的是所有方法

data=json.dumps(dictpayload)等同于json=dictpayload      dictpayload是一个字典
(data=payload)所以data和json传递数据最好以字典形式去做,payload请求体最好构建成字典,也可以构建成字符串,看具体情况
用data还是json取决接口本身的type类型,**

25:requests库请对应的方法和相应的参数:(request库最基本的四种方法)

参数\函数         get     post(提交)     put(修改)     delete(删除)
url          有      有          有          有
params       有
data                  有           有          有
json                  有
headers       有      有          有          有
cookies        有      有          有          有
params参数是get方法独有的

26:requests库响应消息体四种格式:(打印的信息)

四种返回格式:               说明                  用处
r.text:文本响应内容         返回字符串类型                获取页面html
r.content:字节响应内容       返回字节类型(byte)            下载图片或者文件,r.content返回的是字节流
r.json:json解码响应内容      返回字典格式                明确服务器返回json数据才能用
r.raw:原始响应内容         返回原始格式(和fiddler一样,抓取源数据)

27:request库的使用,查看请求信息和响应信息,设置编码等

r.status_code            得到响应状态码
r.headers               打印出响应头,获取一个字典
r.cookies               获取cookies  
reps.text              打印响应体
reps.json()             查看响应体,返回一个json格式   
r.request.headers          打印请求头
r.request.body            打印请求体
reps.encoding=“unicode_escape” 编码设置,为了看到中文(设置响应的编码格式)

28:get可以post来提交,get参数写到/后面去,

**29:**cookie值的获取和传递操作实战

一:直接获取cookie对象操作,如果不需要对cookie进行任何操作封装直接这样取值

reps.cookies      获取的是一个cookie对象,不能改

在login函数里面把****reps.cookies获取到的的cookie对象return返回出去给调用的函数使用

外面拿到这个cookie对象后直接cookies=cookie这样传递就行,不需要二次封装

reps=requests.post(lesson_url,data=payload,cookies=cookie)

logim.py
**import requests
import json
from api接口自动化.teachTest.config import HOST
def login(inData):
url = f"{HOST}/api/mgr/loginReq"
header = {“Content-Typ”: “application/x-www-form-urlencoded”}    #表单传递
payload = inData                 #后面账号密码可能变化,写成参数
reps = requests.post(url, data=payload, headers=header)
reps.encoding = ‘unicode_escape’
return reps.cookies               #直接可以取响应的cookie对象,

#return reps.headers # 返回是个字典
#return reps.headers[‘Set-Cookie’] #返回:sessionid=twenk9lrxixxplrlfqjusmlq6m1dxvfs; HttpOnly; Path=/(返回sessionid=“”)
#return reps.cookies[‘sessionid’] #通过cookie对象取值sessionid值(cookie对象本身带sessionid的)
#通过cookie对象本身的键取获取sessionid值

if __name__ == ‘__main__’:
res=login({“username”:“auto”,“password”:“sdfsdfsdf”})
print(res)

lesson.py
import requests
import json
from api接口自动化.teachTest.config import HOST
from login import login

#cookie值获取了怎么用  cookies=cookie,直接传的cookie对象,cookies=cookie就行,cookie对象传递不需要cookie封装到头
#如果是获取的cookie的值,字符串类型那就需要封装到头
#固定的cookie对象,不需要二次封装,直接取cooke,获取得cookie对象,就可以直接cookies=cookie传递
cookie=login({“username”:“auto”,“password”:“sdfsdfsdf”})   #直接获取cookie对象得方法

lesson_url =f"{HOST}/api/mgr/sq_mgr/"
Host = ‘http://127.0.0.1:80’
api_url = f’{Host}/api/mgr/sq_mgr/’
header = {‘Content-Type’: ‘application/x-www-form-urlencoded’}
payload={
“action”:“add_course”,
“data”:‘{“name”:“初中化学01”,“desc”:“初中化学课程”,“display_idx”:“4”}’
}

reps=requests.post(lesson_url,data=payload,cookies=cookie)
reps.encoding = ‘unicode_escape’
print****(reps.text)**

二:通过响应头headers里面的set_cookie获取sessionid值

通过响应头获取cookie里最重要的sessionid来封装cookie,如果cookie需要做二次封装,自带的cooike不够,可以这样做

cookie在响应头里面  a=reps.headers   返回的是一个字典

import requests
import json
from api接口自动化.teachTest.config import HOST
def login(inData):
url = f"{HOST}/api/mgr/loginReq"
header = {“Content-Typ”: “application/x-www-form-urlencoded”}    #表单传递
payload = inData                 #后面账号密码可能变化,写成参数
reps = requests.post(url, data=payload, headers=header)
reps.encoding = ‘unicode_escape’
return reps.headers[‘Set-Cookie’]     #得到的是一个 sessionid=md31vzd54tvqut8eaxohxl9pjedeujk8; HttpOnly; Path=/这种类型
#后面还要自己转化

三:通过cookie对象获取sessionid值

获取set-cookie的sessionid值或者cookies的sessionid值来自行封装cookie(cookie需要加东西可以这么做),

二次封装cookie这样搞,cookie里面最关键的sessionid值取出来需要自行封装cookie,

reps.headers      返回是个字典,我们要的sessionid在set-cookie里面,

login函数return reps.headers[ ‘Set-Cookie’] 得到sessionid=md31vzd54tvqut8eaxohxl9pjedeujk8; HttpOnly; Path=/

return reps.cookies[‘sessionid’]   也能直接获取sessionid的值,

cookie对象本身带sessionid的,通过键本身获取得到:bm2k2n3eyo0r09jpuie8kkcjjd63gzob  sessionid的值

取得干净地sessionid值,reps.cookies[‘sessionid’]这种方法最方便,不需要任何处理,

得到sessionid值自己封装cookie:

user_sessionid=login({“username”:“auto”,“password”:“sdfsdfsdf”})

user_sessionid=bm2k2n3eyo0r09jpuie8kkcjjd63gzob        获取用户得sessionid值

sessionid一般在cookie里面的,sessionid是cookie得一部分,把user_sessionid值封装成一个cookie

user_cookie={“sessionid”:user_sessionid} 这就把cookie封装好了   先获取sessionid然后自行封装的

如果cookie需要加东西,这样直接自己在封装cookie里添加: user_cookie={“sessionid”:user_sessionid,“name”:“ywt”}怎么加都行,灵活,

这时候直接发送请求附加自己组装的cookie:

reps=requests.post(lesson_url,data=payload,cookies=user_cookie)

import requests
import json
from api接口自动化.teachTest.config import HOST
def login(inData):
url = f"{HOST}/api/mgr/loginReq"
header = {“Content-Typ”: “application/x-www-form-urlencoded”}    #表单传递
payload = inData                 #后面账号密码可能变化,写成参数
reps = requests.post(url, data=payload, headers=header)
reps.encoding = ‘unicode_escape’
return reps.cookies.
[‘sessionid’]****

30:一般头部header和cookie等都封装成字典,灵活,字符串不一定能识别(请求头和响应头一般都是字典)

31:token值得获取和传递操作实战

一:获取简单的token,不加密的token(明码的token)

**import requests
from random import randint
import json
import pprint # 这个函数只能完美打印字典的数据类型
HOST = “http://47.96.181.17:9090” # 主机地址

简单的token,不加密的token(明码的token)

1获取token值

def get_token():
token_url = f"{HOST}/rest/toController"
header = {“Content-Type”: “application/json”} # 头部字典类型
payload = {“userName”: “J201903070064”, “password”: “362387359”}
reps = requests.post(token_url, json=payload, headers=header)

pprint.pprint(reps.json())                        #字典类型可以使用pprint完美打印

return reps.json()[‘token’]

2新增用户

def add_user():
adduser_url = f"{HOST}/rest/ac01CrmController"
token = get_token()
print(token)
header = {“Content-Type”: “application/json”, “X-AUTH-TOKEN”: token}
payload = {“aac003”: “张三”,
“aac004”: “1”,
“aac011”: “21”,
“aac030”: f"135{randint(11111111, 99999999)}", # 随机传入号码值
“aac01u”: “88002255”,
“crm003”: “1”,
“crm004”: “1”,
“crm00a”: “2018-11-11”,
“crm00b”: “aaaaaa”,
“crm00c”: “2019-02-28”,
“crm00d”: “bbbbbb”
} # 字典类型

token一般在头部传递,

reps = requests.post(adduser_url, json=payload, headers=header)
return reps.json()

if __name__ == ‘__main__’:
pprint.pprint(add_user())**

二:加密的token  token加密+上传文件接口

上传文件接口请求头需要Cookie: token=通过获取 token 接口获取(cookie里面放sessionid还是token都可以,看开发和接口文档)
如果判断不了,可以使用fiddler抓个包查看

**import requests, json
import hashlib                 # 这是加密库

def get_md5_data(psw):            # MD5加密–password String md5(‘zr’+111111 +‘hg’)
password = f"zr{psw}hg"
md5 = hashlib.md5()            #1:创建一个md5对象         
md5.update(password.encode(“utf-8”)) #2:完成加密,updata方法加密   对象.方法(需要加密的对象.encode(“utf-8”)),加密时候最好设置编码

对字符串进行编码后再进行一个加密

md5(b"zr111111hg") #也可以传二进制数据直接进行编码:如下

import hashlib

md5 = hashlib.md5()

md5.update(b’zr11111111hg’)

print(md5.hexdigest())

方法二:一行也可以写

print(hashlib.md5(b’zr11111111hg’)).hexdigest()—这样写也可以,(传bytes类型。可以这么写)

3要输出结果,return

return md5.hexdigest()

1:获取接口需要的token

HOST = “http://121.41.14.39:2001”

def get_token(inname, inpsw):
token_url = f"{HOST}/token/token" # url
header = {“Content-Type”: “application/x-www-form-urlencoded”}     # 请求头,封装成字典

password String md5(‘zr’+111111 +‘hg’)----password需要md5加密

打开md5加密网页(百度查询)  把“zr111111hg”加密码提取出来:5c4fcc5add5d087de1e8534189c687f7

md5加密网站;http://tools.jb51.net/password/CreateMD5Password/

payload = {“mobile”: inname, “password”: get_md5_data(inpsw)}
reps = requests.post(token_url, data=payload, headers=header)
return reps.json()[“data”] # 这里的data就是我们要的token

2:文件上传接口

post方法,文件上传接口,先抓个包

Content-Type: multipart/form-data; boundary=WebKitFormBoundaryLpsjAVSe95yonybu–文件上传有个随机参数boundary,算法可以做

做文件接口一般不带这个type,也不带头,除非真的校验,要去找对应的算法—麻烦(传type会有问题)

文件body—

------WebKitFormBoundaryLpsjAVSe95yonybu

Content-Disposition: form-data; name=“file”; filename=“QQ图片20201009011422.png”

Content-Type: image/png

name=“file”—你传给那个变量,文件名

filename=“QQ图片20201009011422.png”—文件对象

Content-Type: image/png 文件类型

文件不要写绝对路径(写相对路径)—不然代码移植很麻烦

def file_doUpload():
file_url = f"{HOST}/user/doUpload"

{变量:(文件名,文件对象,文件的类型)} ----文件对象需要open打开,open函数返回文件对象—文件对象有三个部分

文件的打开不能用read,会乱码,只能用rb模式打开,二进制模式打开,读出是bytes字节的

传文件的话一般这样做的需要–文件变量(文件对象)–组装好

payload = {“file”: (“QQ图片20201009011422.png”, open(“…/data/QQ图片20201009011422.png”, “rb”), “jpg/png/gif”)}
reps = requests.post(file_url, files=payload)
print****(reps.json())

file_doUpload()**

32:测试用例的设计

大部分是功能的用例:中文描述很多,用例为人设计,前置条件—步骤–预取结果–实际结果
自动化用例设计不同  用例为代码设计(代码好处理才行)  用例需要数据分离
用例设计需要了解功能,有哪些模块得知道,

33:自动化测试用例设计:代码好处理,接口来说,需要

1:url
2:body
3:预期结果
4:请求方式
5:路径
6:接口名称
7:用例名称
8:模块
9:用例id
10:请求参数类型
11:请求参数)
代码构建请求可能用到的,自动化测试用例要让代码容易解读,
为什么需要用例名称:如果一个模块有很多接口,比如新增,列出,删除课程,一般按照顺序来测试,
如果不按照顺序(有名称可以自动识别用例模块,那个接口的,接口用例写的时候打乱,有用例名称
可以自己识别组装成列表)—一般接口用例按照模块顺序写,方便

34:前后端分离项目才好做接口测试,api接口交互来做的,(接口返回对应数据json比较多)

前后端不分离发一个接口数据返回在html页面,数据很多,n个单词,不知道接口数据在哪里(可能在某个元素里面)做起来麻烦
接口自动化偏向前后端分离的项目(返回数据格式比较适合,html捞取数据很麻烦)

35:接口用例

最关键的用例多少个取决于请求body不同,请求body里有必填,可选填,每个参数有指定的类型和长度
比如:要求name必须中文的,必填参数,不能有非法字符,有位数,个数,长度,编码设置(int,字符串都有现在)等限制
我们做用例很多时候针对接口用例的数据一般写字典,方便(data和json都能传字典,不需要代码里再转换)
swager框架可以导出对应的接口文档,没有可以导出来–(如果什么都没有,自己写文档,)

36:接口项目代码大概框架如下

teach_sq     项目名称,项目根目录
data      放接口测试用例数据(用例和配置分开,如果还要配置文件放个config包)
lib      放封装接口代码的(原代码),按照不同模块api,web的对应去封装
api_lib
lesson.py
login.py(登录模块)
app_api
web_ui
report        测试报告文件夹
test_case       测试用例文件夹
tools         tools:第三方封装工具代码  如加密或者算法模块
execlMethod.py  excel用例表的读取和写入操作
get_MD5.py
conf      配置文件
config.py
log       日志模块

37:接口测试代码实战

一:tools文件夹源代码:封装第三方封装工具代码-如加密或者算法模块

**execlMethod.py      
import xlrd               #读excel模块
from xlutils.copy import copy     #写excel(复制excel)

import xlutils

#1:_读取excel的数据(简单的功能可以直接写函数就行,没必要写成类),
def get_excelData(sheetName,startRow,endRow,body=6,reps=8):
‘’’
读取excel表函数 ,读取多行的body和reps参数,返回列表套元组的数据[(),(),()]
:param sheetName: 打开的表名
:param startRow: 读取excel开始行
:param endRow: 读取excel结束行
:param body: 读取excel的哪一列,获取body传参
:param reps: 读取excel的哪一列,获取预期响应数据
:return:
‘’’
excelDir=“…/data/测试用例-v1.4.xls”
workBook=xlrd.open_workbook(excelDir) #打开excel创建一个excel对象
#sheets=workBook.sheet_names()   #看有几个表

#2取对应的表sheet来操作
worksheet=workBook.sheet_by_name(sheetName) #取哪个表传表名sheetName就行

#worksheet.row_values(0) #这是读取一excel表的一整行的,返回一个列表
#我们不需要读取一整行,只需要请求参数,和返回结果就行,读单元格

#获取单元格(单元格怎么获取—表.单元对应的值(行,列))
#return worksheet.cell(1,6).value #2行7列获取的是请求的body #.value代表单元格的值
#return worksheet.cell(1,8).value #2行9列获取的是预期的返回结果
#print(worksheet.nrows) #获取行数,获取所有的行(如果用例一个模块很多接口写在excel里面,新增课程,删除课程,各种用例在一起
#这种获取所有行不可行 )
#所以读取excel用例可以考虑传参,开始多少行和结束多少行
reslist=[]
for one in range(startRow-1,endRow):
#获取每个单元格
reslist.append((worksheet.cell(one,body).value,worksheet.cell(one,reps).value))
return reslist

#2:写入数据(现在没有用框架,把结果写在excel表里面)
def set_excelData(sheetIndex,startRow,endRow,colNum,inData,excelOutDir=“…/report/res.xls”): #inData传列表或者元组可迭代数据,写很多数据
excelDir=“…/data/松勤-教管系统接口测试用例-v1.4.xls”
workBook=xlrd.open_workbook(excelDir,formatting_info=True) #打开excel创建一个excel对象
#formatting_info=True,保证excel格式一致(不破坏原表格式)
#格式也复制过去(不破坏原表格式)
newWorkBook=copy(workBook)                    #把打开的excel复制一个全新的workBook对象 (有的只写不读,有的只读不写),另存其他文件这么写
newWorksheet=newWorkBook.get_sheet(sheetIndex)        #newWorksheet全新的excel文件表对象,
#newWorkBook.get_sheet(sheetIndex)获取对应的编号(第一个表)
#获取全新的workbook对象的需要写结果的表对象(表名:sheetIndex传参)

idx=0
for one in range(startRow - 1, endRow): #如果写数据不是从2行开始,从10行开始,range(10,12),indata[one]会越界
newWorksheet.write(one,colNum,inData[idx]) #写单元格,表里面的write写,write(行,列,数据)
idx=idx+1
newWorkBook.save(excelOutDir) #保存路径(保存对象)

if __name__ == ‘__main__’:
print(get_excelData(‘1-登录接口’, 2, 5))
set_excelData(‘1-登录接口’, 2, 5, 9, (1,2,3,4))

get_MD5.py
#time:2020/10/9
import hashlib           # 这是加密库
def get_md5_data(psw):       # 自己MD5加密–password String md5(‘zr’+111111 +‘hg’)
password = f"zr{psw}hg"

1:首先创建一个md5对象

md5 = hashlib.md5()

2:完成加密,updata方法加密

md5.update(password.encode(“utf-8”)) # 对象.方法(需要加密的对象.encode(“utf-8”))----加密时候最好设置编码

对字符串进行编码后再进行一个加密

md5(b"zr111111hg") #也可以传二进制数据直接进行编码:如下

import hashlib

md5 = hashlib.md5()

md5.update(b’zr11111111hg’)

print(md5.hexdigest())

方法二:一行也可以写

print(hashlib.md5(b’zr11111111hg’)).hexdigest()—这样写也可以,(传bytes类型。可以这么写)

3要输出结果,return

return md5.hexdigest()**

二:lib源代码文件夹

api_lib login.py import requests import json from config import HOST import json from tools.get_MD5 import get_md5_data       #加密函数 class Login: def login(self,inData,inFlage=True):      #登录方法,实例方法 url = f"{HOST}/api/mgr/loginReq" header = {“Content-Typ”: “application/x-www-form-urlencoded”} payload = json.loads(inData) #多这步转换,万一inData需要转换,转成字符串还是什么 reps = requests.post(url, data=payload, headers=header) reps.encoding = ‘unicode_escape’ if inFlage True:             #为true默认返回cookie的,这个登录的接口会作为后续接口的前置条件,需要返回cookie return reps.cookies[“sessionid”] elif inFlageFalse:            #本身登录接口需要调用校验是否成功 return reps.json() if __name__ == ‘__main__’: login1=Login() print(login1.login(‘{“username”: “auto”, “password”: “sdfsdfsdf”}’, False)) #现在调用函数需要传字符串,因为里面有payload = json.loads(inData)     indata只能传字符串,里面再转成字典

三:test_case  现在还没有使用到pytest框架,直接把执行的结构写到excel文件夹里

**test_login.py
#自动化执行excel表用例

#1:先获取对应的用例数据
import json
from tools.execlMethod import get_excelData,set_excelData
from lib.api_lib.login import Login
testData=get_excelData(‘1-登录接口’, 4, 5)

2:数据请求获取对应的数据(跑数据)

reslist=[]
for line in testData:
#3构建登录请求
res =Login().login(line[0],inFlage=False)

#4:判断实际结果和预期结果 if
if res[‘retcode’]==json.loads(line[1])[‘retcode’]:
res=“pass”
else:
res = “fail”
reslist.append(res)
#3:写数据到excel
set_excelData(‘1-登录接口’,4,5,9,reslist)**

38:接口api编写心得:

编写某个模块的源代码的时候,需要考虑这个模块有没有前置条件,
一个模块最好编写一个类函数,看这个类函数里面所有的接口函数特点(模块里面所有接口url是不是一样的,(可以创建课程对象的时候就创建出来,定义成实例对象)
sessionid值,cookie需要sessionid封装—每个接口都需要sessionid(创建课程实例也可以传sessionid封装好一个cookie))

39:get请求两种写法

1:参数可以跟到url后面
2:params参数也可以做

40:面试自动化做到什么程度

1:规范流程
所有得代码传git——>git提交后通过ginkins去触发,自动化角度(脚本去触发,项目版本发布触发自动化脚本执行))
需要搭建服务器(搭建持续集成服务器,cicd),所有的代码环境都在服务器里面
开发角度:版本迭代发布到git进行jinkins自动打包,触发自动化执行
自动化人员角度:通过git传自动化脚本,触发对应场景触发自动化脚本执行
版本变化+脚本变化(都能触发自动化脚本执行)
2:本机跑项目(没有上jinkins持续集成的线路)
代码跑完了生成报告后发邮件(代码仅一个人有)
线上  线下

41:http和web socket

有些接口不一定有requset库,用socket去发的,socket会话,通信(人工智能,物联网,通信比较多,实时性强)

42:yaml    专门用来做配置文件的

**一:yaml格式简介(yaml文件非常简洁)
yaml的意思是:yet anoter markup language(仍是一种置标语言)的缩写
yaml是专门用来写配置文件的语言,非常简洁强大,远比json格式方便
可以用yaml做自动化测试框架的配置文件或者用例文件
yaml作配置文件config file  比如说运行版本参数或者设置on和off都可以使用yaml来做

二:python搭建yaml环境:
pyyaml是python的一个专门针对yaml文件操作的模块,
pip install PyYaml

三:python操作yaml库
操作yaml文件主要两大步:读和写
pyyaml 5.1之后,通过禁止默认加载程序(FullLoader)执行任意功能,该load函数也变得更加安全**

**import yaml
yaml_str = “”"
name: xintian
age: 20
ob: Tester
“”"
res_dict1 = yaml.load(yaml_str,Loader=yaml.FullLoader)
print(res_dict1)
print(type(res_dict1))

#控制台----输出
{‘name’: ‘xintian’, ‘age’: 20, ‘job’: ‘Tester’}
<class ‘dict’>**

**四:yaml实际操作
项目工程里面创建一个config文件夹
config文件夹:(配置参数文件  普通文件夹)python文件放包里面,其他文件放文件夹里面(一个有init一个没有)
teach_sq
config
test.yaml       yaml配置文件

五:yaml格式语法
基本规则:
大小写敏感
使用缩进表示层级关系(没有大括号或者小括号)
缩进时不允许使用Tab,只允许使用空格
缩进的空格数目不重要,只要相同层级的元素对齐即可
#表示注释,从它开始到行尾都被忽略
yaml中的值有以下基本类型
字符串

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注软件测试)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

41:http和web socket

有些接口不一定有requset库,用socket去发的,socket会话,通信(人工智能,物联网,通信比较多,实时性强)

42:yaml    专门用来做配置文件的

**一:yaml格式简介(yaml文件非常简洁)
yaml的意思是:yet anoter markup language(仍是一种置标语言)的缩写
yaml是专门用来写配置文件的语言,非常简洁强大,远比json格式方便
可以用yaml做自动化测试框架的配置文件或者用例文件
yaml作配置文件config file  比如说运行版本参数或者设置on和off都可以使用yaml来做

二:python搭建yaml环境:
pyyaml是python的一个专门针对yaml文件操作的模块,
pip install PyYaml

三:python操作yaml库
操作yaml文件主要两大步:读和写
pyyaml 5.1之后,通过禁止默认加载程序(FullLoader)执行任意功能,该load函数也变得更加安全**

**import yaml
yaml_str = “”"
name: xintian
age: 20
ob: Tester
“”"
res_dict1 = yaml.load(yaml_str,Loader=yaml.FullLoader)
print(res_dict1)
print(type(res_dict1))

#控制台----输出
{‘name’: ‘xintian’, ‘age’: 20, ‘job’: ‘Tester’}
<class ‘dict’>**

**四:yaml实际操作
项目工程里面创建一个config文件夹
config文件夹:(配置参数文件  普通文件夹)python文件放包里面,其他文件放文件夹里面(一个有init一个没有)
teach_sq
config
test.yaml       yaml配置文件

五:yaml格式语法
基本规则:
大小写敏感
使用缩进表示层级关系(没有大括号或者小括号)
缩进时不允许使用Tab,只允许使用空格
缩进的空格数目不重要,只要相同层级的元素对齐即可
#表示注释,从它开始到行尾都被忽略
yaml中的值有以下基本类型
字符串

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注软件测试)
[外链图片转存中…(img-wpkrfxxG-1713595957696)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值