Jmeter接口测试

Jmeter的基本使用

Jmeter简介

JMeter最初是由Apache软件基金会的Stefano Mazzocchi编写和开发的, 所以Jmeter也称为“Apache JMeter”,它是一个开源的,100%基于Java的应用程序,带有图形界面。

Apache JMeter 是 100%纯 java 桌面应用程序,被设计用来测试客户端/服务器结 构的软件(例如 web 应用程序)。它可以用来测试包括基于静态和动态资源程序 的性能,例如静态文件,Java Servlets,Java 对象,数据库,FTP 服务器等等。 JMeter 可以用来在一个服务器、网络或者对象上模拟重负载来测试它的强度或 者分析在不同的负载类型下的全面性能。

另外,JMeter 能够通过让你们用断言创建测试脚本来验证我们的应用程序是否 返回了我们期望的结果,从而帮助我们回归测试我们的程序。为了最大的灵活性, JMeter 允许我们使用正则表达式创建断言。

Jmeter的主要功能有: 

1. web自动化测试
2. 接口测试
3. 性能测试
4. 压力测试
5. 通过jdbc进行数据库测试
6. java测试

Jmeter的优缺点 

优点:

1、开源工具,可扩展性非常好

2、高可扩展性,用户可自定义调试相关模块代码

3、精心简单的GUI设计,小巧灵活

4、完全的可移植性和100%纯java

5、完全swing和轻量组件支持(预编译的HAR使用javax.swing.*)包

6、完全多线程框架,允许通过多个线程并发取样以及单独的线程对不同的功能同时取样

7、支持脚本取样器

缺点:

1、 不支持IP欺骗
2、 使用JMeter无法验证JS程序,也无法验证页面UI,所以要须要和Selenium配合来完成Web2.0应用的测试

了解:JDK、JRE、JVM

JDK

JDK概念:java开发工具包,程序员使用。包含JRE和JVM。

JRE

JRE(java runtime environment):JAVA程序运行环境,包含JVM和JVM运行时所需要的资源。是java编写的程序运行时需要的软件环境, 是提供给想运行java程序的用户使用的.

JVM

JVM(java virtualenv machine):Java虚拟机

思考:

​ JDK 和JRE的区别?

JAVA跨平台原理【扩展】

java跨平台机制是: 一次编译,到处运行.

 总结: 对于测试人员,我们要记住,如果使用JMeter,必须要安装JDK或者JRE。

jdk安装参考:

jdk环境安装参考文档

搭建Jmeter环境

Jmeter下载与安装

官网下载地址:

Apache JMeter - Download Apache JMeter

下载示意图(安装包)

注意:

下载后,解压文件到任意目录,避免在一个有空格的路径安装Jmeter,这将导致远程测试出现问题。

启动JMeter的两种方式:

  • 进入bin目录

    • 方式1: 双击 ApacheJMeter.jar文件(最常用);

    • 方式2: 双击 Jmeter.bat文件(有黑窗口并且不能关闭,是服务端);

    • 方式3: linux 下启动Jmeter == meter.sh

Jmeter常用目录文件介绍

Bin目录

存放可执行文件和配置文件

  • Jmeter.bat:windows系统中JMeter的启动文件
  • ApacheJMeter.jar Java环境下的JMeter启动文件
  • Jmeter.log:日志文件
  • Jmeter.sh:linux系统中JMeter的启动文件
  • Jmeter.properties:系统配置文件
  • Jmeter-server.bat:windows分布式测试要用到的服务器配置
  • Jmeter-serve:linux分布式测试要用到的服务器配置

docs目录(了解开源)

docs:是JMeter的java Doc,可打开api\index.html页面来查看;

printable_docs目录

printable_docs的usermanual子目录下的内容是JMeter的用户手册文档,其中usermanual下component_reference.html是最常用到的核心元件帮助文档。

注意:lib文件夹也是一个常用文件夹,使用时再讲。

Jmeter基本使用流程

主界面布局 

JMeter的主界面布局分为标题栏、菜单栏、工具栏、树形标签栏和内容栏

标题栏:主要显示计划信息及JMeter版本。
菜单栏:全部的功能的都包含在菜单栏中。
工具栏:工具栏中的按钮在菜单栏都可以找到,工具栏就相当于菜单栏常用功能的快捷按钮
树形标签栏:树形标签栏通常用来显示测试用例(计划)相关的标签。
内容栏:配合树形标签栏显示,树形标签中点击哪个标签,内容栏中就显示相应的内容和操作。

组件

概念: Jmeter中功能点称为组件

元件

概念:相同类似功能组件的集合称之为元件.

常用的八大元件:

  • 逻辑控制器
  • 配置元件: 包含java默认值、http请求默认值、http表头管理器等等
  • 定时器(Timer): 包含同步定时器、泊松随时时间等
  • 前置(预)处理器: jdbc 预处理器、html链接解析器,用户参数等
  • 采样器(Sampler): 包含http请求、jdbc请求、邮件请求等
  • 后置处理器: 包含jdbc后处理器、xpath抽取器、正则表达式抽取器等
  • 断言
  • 监听器: 包含图表结果、查看结果树、汇总报告等

案例:发送一个请求 

测试计划要先保存

 创建线程组

 设置线程组选项

 在线程组上选择HTTP请求

设置服务器,路径 

 设置结果树

 运行

 查看结果

设计结果为Json 

 

 创建第二个线程组

但是发现请求的顺序不对,在测试计划中勾选下面即可解决 

 清除请求

Jmeter的基本使用流程

需求:

1. 需求对我们学院查询执行100次,如何去做?
2. 50个请求同时请求如何操作?

使用JMeter的解决方案

1. 添加【测试计划】
2. 基于添加的测试计划添加【线程组】,循环次数设置为100次
3. 在【取样器】中基于线程组添加**HTTP请求**  
4. 在【监听器】基于线程组添加【察看结果树】  
5. 在监听器基于线程组添加【聚合报告】

创建测试计划 

在测试计划的名称栏目修改名字后,ctrl + s 保存

设置请求三要素 

 运行并查看结果

设置监听器和结果树

查看运行结果

相关概念

TestPlan(测试计划)

在Jmeter中包含各种相互关联但为不同目的而设计的元素.

测试计划可视化为用于运行测试的JMeter脚本, 包含执行脚本的所有步骤。测试计划由测试元素组成,包括线程组,逻辑控制器,样本生成控制器,监听器,定时器,断言和配置元素等等。

每个测试计划中至少应有一个线程组。 我们可以根据要求添加或删除元素。

测试计划的作用:
1. 本次测试所需要的【组件】都是基于测试计划添加;
2. 本次测试所有组件的设置与执行都基于测试计划;

组件:完成指定功能代码段的封装;
选项(在这里我们只介绍我们会使用到的选项)
  • 独立运行每个线程组:

    进程:是每个正在运行的应用程序。
    线程:按照进程的指令去执行指定的代码。
    线程组(多线程):多个线程的组合。
    线程组(多线程)的执行顺序是并行的。 
    
    勾选:让本次测试计划中所有线程组保持从上到下顺序执行
    
  • Add directory or jar to classpath:

    加载第三方jar包;比如:测试数据库时使用,加载数据库驱动jar包。
注意事项
  • 在运行测试计划前,一定要保存测试计划

  • Jmeter文件或测试计划以.jmx扩展文件形式保存.JMX是一种基于开放测试的格式,它使测试计划能够在文本编辑器中启动.

Threads(User)线程组 【重点】

1. thread group(线程组): 通常添加使用的线程组,一般一个线程组可以看作一个虚拟用户组,其中每个线程为一个虚拟用户.
2. setup thread group: 一种特殊线程组, 可用于执行预测试操作,即执行测试前进行, 一般做初始化操作.
3. teardown thread group: 一种特殊的线程组, 可用于执行后操作,即执行测试结束后执行, 一般做结尾操作.
thread group(线程组)

作用:
1. 添加测试中使用的大多数组件
在取样器错误后要执行的动作
- 继续: 取样器执行时出现错误时,请求不会停止,继续执行
- Start Next Thread Loop: 忽略错误, 线程当前循环错误,执行下一个循环
- 停止线程: 只限当前线程停止,不影响其他线程执行
- 停止测试: 当前执行的线程全部执行完毕后结束
- Stop Test Now: 立刻停止
线程属性
- 线程数:虚拟用户数
- Ramp-Up Period(in serconds):启动虚拟全部用户数所需要的时间(每个虚拟用户平分时间)
- 循环次数 :指定次数或勾线永远
- Delay Thread creation until needed: 默认不勾选,测试开始时,所有线程被创建完.若勾选,线程会在需要合适时间进行创建.
- 调度器:勾选后,调度器配置才能使用;
调度器配置
- 持续时间(秒):设置脚本压测持续时间
- 启动延迟(秒):启动延迟时间 
- 启动时间: 设置启动时间和启动延迟冲突.如果启动延迟设置后,启动时间被忽略.
- 结束时间: 设置脚本运行结束时间,和持续时间冲突,若有持续时间,结束时间会被忽略

提示:为了理解setup thread、teardown thread两个线程组我们结合案例理解下

案例
需求:
通过thread group、setup thread group、teardown thread group 三个线程组去访问去访问查询所有学院接口

效果图

需求组件:
1. 基于测试计划添加【线程组】  
2. 基于线程组添加【HTTP请求】
3. 基于测试计划添加【setup thread group】线程组
4. 基于setup thread group添加HTTP请求
5. 基于测试计划添加【teardown thread group】线程组
6. 基于teardown thread group添加HTTP请求
7. 基于测试计划添加【察看结果树】
组件设置细节:
1. 测试计划勾选独立运行线程组 
2. HTTP请求:服务器地址 http://127.0.0.1:8000/api/departments/
3. HTTP请求: 修改HTTP请求名称,以做区分
结论
经过测试我们发现线程组的执行顺序是: setup thread group、thread group、 teardown thread group

Http请求

添加http请求

http界面

作用:
1. 模拟前端或第三方软件向服务器发送请求;
2. 设置请求时的方法和参数数据;
参数详解:
1. 名称:本属性用于标识一个取样器,建议使用一个有意义的名称。
2. 服务器名称或IP :HTTP请求发送的目标服务器名称或IP地址。
3. 端口号:目标服务器的端口号,默认值为80 。
4. 协议:向目标服务器发送HTTP请求时的协议,可以是http或者是https ,默认值为http 。
5. 方法:发送HTTP请求的方法,可用方法包括GET、POST、PUT、DELETE。
6. Content encoding :内容的编码方式,默认值为iso8859;一般设置【UTF-8】
7. 路径:目标URL路径(不包括服务器地址和端口)
8. 同请求一起发送参数:请求时需要传递参数,如:学院资源list查询
         http://127.0.0.1:8000/api/departments/?$dep_id_list=T01,T02,T03
         参数名称:【$dep_id_list】
         参数值:T01,T02,T03
Body Data选项作用:
1.新增或更新时需要传递JSON报文;如学院新增是的JSON报文填写位置:
       {
          "data": [
                      {
                        "dep_id": "T01",
                        "dep_name": "Test学院",
                        "master_name": "Test-Master",
                        "slogan": "Here is Slogan"
                      }
                  ]
        }

    2. 【注意】:新增和更新时传入报文也需要设置Content-Type:application/json   
                 告诉服务器我传的数据格式为JSON格式;  
                 设置地点:配置元件-->HTTP信息头管理器(用到的时候我们在讲解)
HTTP请求总结:
1. 接口完整请求地址
2. JSON报文存放地址
3. 设置默认请求数据格式

查看结果树

作用:
1.查看请求服务器时的请求信息;
2.查看服务器响应数据;
3.记录信息到指定文件;
说明:
1. 文件名:存放服务器响应后的状态信息; 如:e:\查询所有response.txt
2. 取样结果:服务器响应的信息头信息;比如:响应代码,响应数据大小
3. 请求:查看向服务器请求时的信息;比如:请求地址、方法、数据等
4. 响应数据:查看服务器响应的数据;比如:获取资源时,返回的JSON数据
察看结果树总结:
1. 查看请求
2. 查看响应
3. 存储请求状态信息

总结

我们发现在Jmeter中测试计划是展开测试工作的源头,所以要掌握测试计划的创建、删除操作; 在测试计划使用中我们根据不同需求创建不同的元件和组件, 来实现测试计划.

元件是相似功能组件的集合,能够便于管理组件,而组件是某个功能的在Jmeter中的实现.

测试计划中每个线程组就是虚拟的一组用户,每个线程就是一个虚拟用户,对于每个用户发起的测试的请求,可以每次定义http请求

Jmeter 八大元件及执行顺序

JMeter的主要元件:

  • 控制器(Controllers)
  • 监听器(Listeners)
  • 计时器(Timers)
  • 配置元素(Configuration Elements)
  • 预处理器元素(Pre-Processor Elements)
  • 后处理器元素(Post-Processor Elements)

元件作用域及执行顺序

jmeter是一个开源的性能测试工具,它可以通过鼠标拖拽来随意改变元件之间的顺序以及元件的父子关系,那么随着它们的顺序和所在的域不同,它们在执行的时候,也会有很多不同.

jemter主要依靠测试计划中元件相对位置,来确定作用域.

元件的肤质关系及元件本身类型决定test plan中各元件的执行顺序;元件在test plan中位置不同,可能导致该元件有很大的行为差异.

元件作用域

在jmeter中,元件的作用域是靠test plan的树形结构中元件的父子关系来确定的,其原则如下:

1)sampler不与其他元件相互作用,因此不存在作用域问题

2)Logic Controller只对其子节点中的sampler和Logic Controller作用

3)除sampler和Logic Controller外的其他元件,如果是某个sampler的子节点,则该元件仅对其父节点作用

4)除sampler和Logic Controller外的其他元件,如果其父节点不是sampler,则其作用域是该元件父节点下的其他所有后带节点(包括子节点,子节点的子节点等)

jmeter中共有8类可被执行的元件(test plan和thread group不属于元件),其中,sampler(取样器)是不与其他元件发生交互的作用的元件,Logic Controller(逻辑控制器)只对其子节点的sampler有效,而其他元件需要与sampler等元件交互。

Config Elements(配置元件):影响其范围内的所有元件

Pre-porcessors(前置处理器):在其作用范围内的每一个sampler元件之前执行

Timer(定时器):对其作用范围内的每一个sampler有效

Post-porcessors(后置处理器):在其作用范围内的每一个sampler元件之后执行

Assirtions(断言):对其作用范围内的每一个sampler元件执行后的结果执行校验

Listener(监听器):收集其作用范围内的每一个sampler元件的信息并且呈现出来

执行顺序

在同一个作用域范围内,测试计划中的元件按照一下顺序执行:
1.配置元件
2.前置处理器
3.定时器
4.取样器
5.后置处理器
6.断言
7.监听器

注意:

  1. Pre-porcessors、Post-porcessors和Assirtions等元件仅对Sampler作用,如在它们作用域内没有任何Sampler,则不会被执行;

  2. 如果在同一作用域范围内有多个同一类型的元件,则这些元件按照它们在test plan中的上下顺序依次执行

八大可执行元件

控制器分为两大类:

  • 取样器
  • 逻辑控制器

作用: 用这些元件驱动处理一个测试

取样器(Sampler)

是性能测试中向服务器发送请求,记录响应信息,记录响应时间的最小单元. JMeter 原生支持多种不同的sampler , 每一种不同类型的 sampler 可以根据设置的参数向服务器发出不同类型的请求.

常用的sampler:

1) HTTP请求
2) JDBC Request 
3) Debug Sampler
逻辑控制器

包含两类元件:

  • 一类是控制Test Plan中Sampler节点发送请求的逻辑顺序控制器,常用的有:If Controller、Swith Controller、Loop Controller、Random Controller等
  • 另一类是用来组织和控制Sampler节点的,如Transaction Controller、Throughput Controller等

监听器

监听器是对测试结果进行处理和可视化展示的一系列组件.

常用的组件:

1) 察看结果树
2) 聚合报告 
3) 断言结果

定(计)时器

作用: 设置操作之间的等待时间. 等待时间是性能测试中常用的控制客户端QPS的手段.

常用:

1) Synchronizing Timer

配置元件(config Element)

作用: 设置相关请求的静态配置.不用每次都重复配置.

常用的:

1) CSV Data Set Config
2) HTTP请求默认值
3) HTTP信息头管理器

案例:设置公用信息

前置处理器(Per Processors)

作用: 在实际请求发出前对即将法术的请求进行特殊处理.

例如:Count处理器可以实现自增操作,自增后生成的的数据可以被将要发出的请求使用,而HTTP URL Re—Writing Modifier处理器则可以实现URL重写

1) 用户参数

后置处理器(Post Processors)

作用: 对sampler发出请求后得到服务器响应进行处理.一般用来提取响应的特定数据.

常用:

1) 正则表达式提取器 
2) XPath Extractor

断言(Assertions)

作用: 用于检查测试中得到的响应数据是否符合预期.Assertions一般用来设置检查点,用以保证性能测试过程中的数据交互与预期一致

常用:

1) 响应断言

注意: test plan和thread group不属于元件

Jmeter核心知识点

参数化

为什么要参数化

现在有下面需求:

学院-新增100条数据:
    1) 请求方法:POST
    2) 请求地址:http://127.0.0.1:8000/api/departments/
    3) 请求JOSN报文:
    4) 调用传入的json串如下(可新增多条,之间用,隔开):
        {
            "data": [
                    {
                        "dep_id": "T01",
                        "dep_name": "Test学院",
                        "master_name": "Test-Master",
                        "slogan": "Here is Slogan"
                    }
              ]
        }

参数化概述

Jmeter参数化的概念

当使用JMeter进行测试时,测试数据的准备是一项重要的工作。若要求每次迭代的数据不一样时,则需进行参数化,然后从参数化的文件中来读取测试数据.

参数化就是动态的获取或设置数据.

参数化:是自动化测试脚本的一种常用技巧,可将脚本中的某些输入使用参数来代替,在脚本运行时指定参数的取值范围和规则。

例如登录时利用GET/POST请求方式传递参数的场景.

脚本在运行时,根据需要选取不同的参数值作为输入,该方式称为数据驱动测试(Data Driven Test),而参数的取值范围被称为数据池(Data Pool)。

参数化的四种方式及使用场景对比

参数化使用场景
1CSV Data Set Config适用于参数取值范围较大的时候使用,该方法具有更大的灵活性
2User Parameters适用于参数取值范围很小的时候使用
3User Defined Variables一般用于Test Plan中不需要随请求迭代的参数设置,如:Host、Port Number
4函数

可作为其他参数化方式的补充项,如:随机数生成的函数${__Random(,,)}

CSV Data Set Config

概念:一种从外部读取数据功能的组件
实施方案分析
基于测试计划->线程组
2. 基于线程组->配置元件->CSV Data Set Config
3. 基于线程组->Sampler->HTTP请求
4. 基于测试计划->HTTP信息头管理器
5. 基于测试计划->监听器->察看结果树
组件要点分析
1. 线程组:循环次数10
2. CSV Data Set Config 读取变量配置
3. HTTP请求:Body Data填写(JSON报文) 方法(POST)
4. 参数化引用格式:${参数名} 如:${dep_id}
5. HTTP信息头管理器:Content-Type:application/json;charset=utf-8
参数配置图

dep.txt

1. Filename:文件路径+文件名+后缀名    如:d:/a.txt;
2. File Encoding:文件编译字符编码,一般设置utf-8;
3. Vaiable Names:读取参数后保存的变量名称;
4. Delimiter:如文件中使用的是逗号分隔,则填写逗号;如使用的是TAB,则填写\t;
5. Allow quoted data: 是否允许引用数据,默认false,选项选为“true”的时候对全角字符的处理出现乱码 ;
6. Recycle on EOF?:是否循环读取参数文件内容;True是循环读取; False时,读取文件末尾就不在继续读取.
7. Stop thread on EOF?:当Recycle on EOF为False时(读取文件到结尾),停止进程,当Recycle on EOF为True时,此项无意义;
8. Sharing mode:共享模式,即参数文件的作用域.
添加http信息头管理器并进行参数配置

作用: 服务器请求的数据格式.

参数配置:

1. Content-Type:指定请求信息格式-类型名称
2. application/json:指定请求信息为-JSON格式
3. charset=utf-8:字符编码
添加http请求

请求结果

总结

1. CSV Data Set Config 配置参数设置
2. 参数化引用格式
3. HTTP请求
4. HTTP信息头管理器作用与设置

用户定义变量

用户可根据需求自定义相应的变量,一般做全局变量使用。

分析:     
    1. 通过概念我们知道,【用户定义的变量】一般做全局变量使用,不适合参数需求量大时的选择
    2. 所以在这里我们不在使用,用户定义的变量去做我们学院新增时的解决方案
    3. 主要讲解下它做参数化时的使用步骤和方式
需求场景
1. 查询学院-指定:http://127.0.0.1:8000/api/departments/T02/ 
2. 接口查询指定的id(T02)采用参数动态获取方式
解决方案
1. 参数化组件:用户定义的变量 (测试计划->线程组->配置元件->用户定义的变量)
2. 线程组 
3. 请求组件:HTTP请求 
4. 查看结果组件:察看结果树
效果图

1. 名称:变量名称(参数化引用时使用)
2. 值:变量的值(通过引用变量名称获取的值)
3. Description:备注说明
总结:
1. 一般做设置全局变量
2. 请求时附带少量参数需求使用

用户参数

一种参数设置方式,用户可设置参数名称以及参数值;

我们使用用户参数作为参数化方式对学院新增10条记录问题进行实现
解决方案实施分析
1. 位置:测试计划-->线程组-->前置处理器-->用户参数 
2. 其他组件和CSV Data Set Config实现新增10个学院相同,去除 CSV Data Set Config组件
组件要点分析
1. 线程组:注意是【线程数】为10
2. 用户参数:学院每个属性为一个参数名称;参数格式:可以是数字、字母、下划线开头,建议最好是实义单词
3. HTTP请求:Body Data填写(JSON报文) 方法(POST)
4. 参数化引用格式:${参数名} 如:${dep_id}
5. HTTP信息头管理器:Content-Type:application/json;charset=utf-8
用户参数配置

1. 添加变量:学院资源属性 (dep_id、dep_name、master_name、slogan)
2. 添加用户:每个线程为没一个用户

用户参数-总结:

1. 用户参数位置
2. 线程组设置
3. 引用参数化方式  
4. 用户参数适合满足少量参数化需求场景使用

函数

函数是完成某个指定功能代码的封装。

1. 函数查找方式:函数助手对话框
       1) 菜单-选项->函数助手对话框
       2) Ctrl+Shift+F1
       3) 工具栏倒数第二个记事本图标
2. 函数在Jmeter中有非常多类型(计数函数、日期函数、随机函数...)
需求:
1. 查询学院-所有:http://127.0.0.1:8000/api/departments/
2. 查询10次,在每次请求地址后面增加访问记录数
    http://127.0.0.1:8000/api/departments/?num=1
    http://127.0.0.1:8000/api/departments/?num=2
需求关键点分析
1. 起个计数参数名 如:num
2. 把计数参数名的值给参数化
3. 参数化的值使用计数函数(count)
实施方案
1. 基于测试计划添加线程组(循环次数10)
2. 基于线程组添加HTTP请求
3. 基于测试计划添加察看结果树
函数配置

1. 选择一个功能:选择_counter计数函数
2. 第一个参数:TRUE,每个用户有自己的计数器;FALSE,使用全局计数器 我们选FALSE
3. 点击生成
4. 选择复制生成的函数
函数的结果图

?num=${__counter(FALSE,)}

1. ?:问号后面一切东东,HTTP请求不做解析
2. num:为我们起的一个计数参数名称
3. ${__counter(FALSE,)}:为我们粘贴进来的函数作为值使用
函数总结:
1. 使用方便快捷
2. 经常配合固定参数使用

参数化方式总结

区别
1. CSV Data Set Config: 功能强大、适应各种迭代及多参复杂场景。
2. 用户参数:适应传递少量参数时使用
3. 用户定义的变量:和用户参数使用场景相似,不同在于一般做全局变量使用
4. 函数:功能强大,函数类型繁多,灵活度大,适应各种应用场景。

推荐

 1. CSV Data Set Config 
 2. 函数

实战案例:

数据库

为什么要使用Jmeter从数据库中获取数据?

1. 我们测试的软件项目所有的数据都存储在数据库

 思路分析

1) 连接数据库
2) 发送sql语句请求进行增删改查
3) 查看执行sql语句后的数据
4) 获取执行sql语句后的数据

注意: Jmeter本身不具备直连数据库的功能,必须整合具备此功能的第三方实现.

案例

使用Jmeter连接学生管理项目数据库,统计学院资源表中记录数; 
数据库名:db.sqlite3
数据库位置:项目\studentManagementSystem\
学院数据库表名:departments

效果图

获取执行sql语句后的数据

操作步骤

连接数据

添加数据库jar包, 添加数据库驱动

配置连接信息

发送sql语句请求
 1) 添加-线程组
 2) 添加-JDBC Request
查看执行sql语句响应数据
1) 添加-察看结果树
获取执行sql语句后的数据
1) 添加-Debug Sampler

参数详解

JDBC Connection Configuration 参数详解

1. Variable Name:数据库池名称【JDBC请求时要引用】
2. Database URL:jdbc:sqlite:E:\\课件\\项目\\studentManagementSystem\\db.sqlite3
         1) jdbc:sqlite:通过JDBC驱动连接sqlite数据库固定格式
         2) E:\\课件\\项目\\studentManagementSystem\\db.sqlite3:数据路径和数据库名
3. JDBC Driver class:org.sqlite.JDBC(sqlite驱动包内sqlite驱动的路径位置)
4. Username:(连接数据库用户名,如实填写,我们项目sqlite数据库用户名默认为空)
5. Password:(数据库密码,如实填写,如果密码为空不写)
jdbc参数详解

JDBC Request 参数:Variable names 设置图

1. Variable Name:数据库连接池的名字,需要与JDBC Connection Configuration的Variable Name【保持一致】
2. Query Type:Select Statement、Update Statement
    1) Select Statement:查询语句时使用
    2) Update Statement:(新增、更新、删除)语句时使用
3. Query: 填写的sql语句未尾不要加“;”  
4. Variable names:count 保存sql语句返回结果的变量名;

数据库-总结:

1. JDBC驱动包
2. 数据库连接池配置
3. JDBC请求
4. Degbeg Sampler

【扩展】对不同类型数据库配置

1. 针对企业内常用数据库如何基于Jmeter连接做个扩展
2. 数据库主要有:Mysql、Oracle
3. 各类型不同之处: 
    1. 数据库驱动包各不相同
    2. 数据库连接池配置各不同

MySQL数据库

1. MySQL驱动包
2. 数据库池配置

 MySQL连接驱动-jar包

mysql-jdbc连接配置

注意

1. 127.0.0.1:为数据库服务器IP地址(根据实际项目数据库服务器IP更改)
2. 3306:为数据库端口号(根据项目实际实际数据库端口更改)    
3. xst_shop:为数据库名称(根据项目实际数据库名更改)
4. Username:为登陆数据库用户名
5. password: 为登陆数据库密码
6. 其他为固定格式,参照配置图填写

Oracle数据库

1. Oracle驱动包
2. 数据库池配置

oracle数据库驱动包

oracle jdbc连接配置

注意
1. 10.245.145.45:为数据库服务器IP地址(根据实际IP地址填写)
2. 1521:为端口号(根据项目端口更改)
3. orcl:为数据库名称(根据实际项目数据库名称填写)
4. Username:为登陆数据库用户名
5. password: 为登陆数据库密码
6. 其他为固定格式,参照配置图填写

Jmeter关联

需求

1. 对http://www.baidu.com进行2次访问;
2. 第一次获取title值,第二次把获取的值作为参数名(title)的参数值附加请求中。
问题
1. 如何从第一次请求获取的响应数据中提取title值?
2. 解决这种需求场景在测试领域中叫什么?
什么是关联?
概念:一个请求响应的结果要作为另一个请求提交的数据.

 注意: 由第一个请求响应的结果,我们在第二次请求中使用, 我们会发现,关联是后置处理器的一部分.

Jmeter关联中常用的两种方式
1. 正则表达式提取器 
2. XPath Extractor

正则表达式提取器 

概念:根据需求定制规则,返回匹配规则的数据的一种组件
实施方案分析 
1. 测试计划->线程组
2. 线程组->HTTP请求(获取title)
3. 获取title->后置处理器->正则表示式提取器
4. 线程组->HTTP请求(使用title)
5. 测试计划->察看结果树

技术难点分析

1. 正则表达式
 实施步骤
1. 创建测试计划、线程组、添加http请求
2. 添加正则表达式提取器

参数解释:

  1. 引用名称:下一个请求要引用的参数名称,如填写title,则可用${title}引用它。
  2. 正则表达式:制定规则的公式 公式模板:前缀识别()后缀识别 ():括起来的部分就是要提取的数据。 1) .:匹配任何字符串。 2) +:一次或多次。 3) ?:在找到第一个匹配项后停止。 4) *:匹配规则内所有字符串
  3. 模板:$$格式;$1$表示解析到的第1个值;如果匹配有2个值,第二个值$2$,以此例推
  4. 匹配数字:1代表全部取值,0代表随机取值,通常情况下填1
传值, 把第一个请求提取的值传给第二个请求, 格式是: ${变量名}
正则表达式提取器-总结
1. 位置
2. 引用名称
3. 正则表达式
4. 模板格式
5. 匹配数字

xpath Extractor(xpath 提取器) 

一种可被用来提取页面给定内容的组件,主要采用的方式为XPath路径
解决方案分析
1. 测试计划->线程组
2. 线程组->HTTP请求(获取title)
3. 获取title->后置处理器->XPath Extractor
4. 线程组->HTTP请求(使用title)
5. 测试计划->察看结果树

实施难点分析

XPath 路径
实施步骤
1. 创建测试计划、线程组、htt请求
2. 添加xpath提取器

参数解释

1. Use Tidy?:当需要处理的页面是HTML格式时,必须选中该选项。
       1) Tidy:一种HTML格式化模板
2. Reference Name:存放提取出的值的参数名称(被引用时使用)。
3. XPath Query:用于提取值的XPath表达式(跟学习WEB自动化使用XPath一样)。
传值.将第一个请求xpath提取的数据传递给第二个请求,格式是:${变量名}
xpath提取器总结
1.xpath的使用
2.use tidy

关联-总结:

正则表达式和xpath的区别
1. 正则表达式提取器可以用于对页面任何文本文档、标签文档的提取,提取的内容是根据正则表达式在页面内容中进行文本匹配;
2. XPath Extractor则可以提取返回页面任意标签元素的任意属性,如//a[@href="http://tieba.baidu.com"]/@name;

选择

1. 如果需要提取的文本是页面上某元素的属性值,建议使用XPath Extractor;
2. 如果需要提取的文本在页面上的位置不固定,或者不是元素的属性,建议使用正则表达式提取器。

注意事项:

关联中使用的正则表达式、xpath提取器是属于后者处理器的, 我们使用的是比较常用的.

更多请参考:后置处理器

断言

断言概述

断言概念
概念:断言就是让程序代替人工去判断程序响应数据是否达到预期结果.
断言作用
在request的返回层面增加一层判断机制,检测是否符合预期,来保证测试过程中数据交互与预期是一致的.
断言的实现
断言组件实现流程
  1. 在Sampler下增加对应的断言(不同类型的断言检查的内容不同)
  2. 配置响应的检查内容
  3. 添加监听器, 通过断言结果查看断言是否通过
    • 通过断言,无显示
    • 失败,除了输出请求失败外,还会输出失败原因

注意事项:

  • 一个Sapmpler下可以添加多个断言,要根据需求添加断言,当所有断言都通过,那么请求才算成功
常用断言组件分类 

最新的3.x版本的jmeter中有13种断言,我们只简介常用的:响应断言、size assertion、Duration Assertion

需求

1.查询学院-指定:http://127.0.0.1:8000/api/departments/T02/ 
  让程序判断请求 
   1) 响应代码是否为200
   2) 响应数据是否为T02资源
2. 使用断言判断需求1返回的主体数据大小是否小于100kb
3. 判断需求1返回的响应时间是否小于10ms

响应断言

响应断言的作用对象
作用:判断响应的报文内容
作用对象包含: 响应文本、响应内容、响应状态码、响应信息等
解决方案
1. 测试计划-线程组
2. 线程组-HTTP请求
3. HTTP请求->断言->响应断言
4. 测试计划->监听器->察看结果树
5. 测试计划->监听器->断言结果
响应断言配置图
响应的状态码

1. 响应代码: 只有选择响应代码,Jmeter才去拿预期结果和响应代码去对比
2. 要测试的模式:这里填写我们的预期结果;如:200
响应的数据

1. 响应文本:只有选择响应文本,Jmeter才去拿预期结果和响应数据去对比
2. 要测试的模式:这里填写我们的预期结果;如:T02
断言结果
断言成功,在结果树上默认不显示,断言失败,显示失败原因

Size Assertion(大小断言)

作用对象
作用:判断返回内容的大小,
作用对象包含: 全部响应、响应头部、响应主体、响应报文等等
解决方案
1. 测试计划-线程组
2. 线程组-HTTP请求
3. HTTP请求->断言->size assertion 
4. 测试计划->监听器->察看结果树
5. 测试计划->监听器->断言结果
size assertion配置图

1. Response Body:选择此项判断返回主题数据Body的大小
2. 字节大小:设置要判断数值 单位bytes/kb
3. <:小于符号,说明判断要返回Body主题数据大小小于100kb

断言持续时间--Duration Assertion

作用对象
作用: 断言服务器响应请求的时间是否小于指定值; 
作用对象: 服务器
 解决方案
1. 测试计划-线程组
2. 线程组-HTTP请求
3. HTTP请求->断言->duration assertion
4. 测试计划->监听器->察看结果树
持续时间配置图 

1. 持续时间(毫秒):这里的持续时间为毫秒,比如:10 毫秒。
2. 断言响应时间是否超过设置的值(10),如果超过则断言失败,反之成功

 总结

断言是使用程序代替人工进行检测程序的响应结果是否和预期一样. 各种不同的断言有不同的使用场景,要灵活使用不同断言.

1. 响应断言:断言响应代码/响应数据
2. Size Assertion:断言响应数据大小是否在指定范围
3. 断言持续时间:断言响应数据时间是否在指定时间范围

集合点

需求

1. 学院查询-所有 http://127.0.0.1:8000/api/departments/
2. 学院查询-指定 http://127.0.0.1:8000/api/departments/T02/

需求对(学院查询-所有)这个接口进行20个用户【同时】请求访问操作。

问题

1. 设置线程组循环次数为:20,解决不了需求问题
2. 设置线程组线程数量为:20,同样解决不了问题

集合点(同步定时器)

集合点的作用
作用: 阻塞线程,直到指定的线程数量到达后,再一起释放,模拟并发.

主要使用场景是并发亮特别大的场景,比如“秒杀”, “抢购”等场景.

需求实现分析
1. 测试计划->线程组
2. 线程组->Sampler->HTTP请求(学院查询-所有)
3. 线程组->Sampler->HTTP请求(学院查询-指定)
4. HTTP请求(学院查询-所有)->定时器->Synchronizing Timer
5. 测试计划->监听器->察看结果树
实现步骤
创建测试计划、添加线程组、通过sampler添加http请求,添加同步定时器
配置同步定时器

参数解释:

1. Number of Simulated Users to Group(模拟用户组数量)
   by:集合多少用户后再执行请求(也就是执行的线程数)

扩展选项:
2. Timeout in milliseconds:超时时间(毫秒)
    1) 设置为0,无限等待,直到达到集合点设置的线程数。
    2) 设置指定时长,如果到达指定时长,集合点数量未到达,集合多少用户释放多少用户数量。
添加聚合报告查看运行结果

添加聚合报告:

集合点作用域
1. 集合点只对一个请求起作用,如果针对指定请求起作用,放到该请求内;
2. 集合点对多个个请求起作用,放到与请求平级同一层次;
集合点-总结

集合点,也叫同步定时器, 作用是阻塞线程的释放,直到线程达到一定数量一次释放,产生并发的压力,从而模拟模拟并发场景.

每个请求对所在的请求起作用,假如要想让一个集合点对多个请求起作用,要调整集合点和请求的层级关系,使其同一个层级.

更多定时器资料请参考:

1. jmeter—定时器

2. 定时器

函数

Jmeter函数概述

Jmeter函数是什么
可以被认为是特殊值,可以填充测试树任何采集器或其他元素的字段.
为什么学习Jmeter函数
  1. 使用函数可以批量对请求的参数操作
  2. 使用函数可以读取外部数据进行参数化
  3. 使用函数可以设置指定动态数据为全局变量
如何使用Jmeter函数
Jmeter函数调用格式
${__functionName(var1,var2,var3)} 

     1) ${} 函数引用固定格式和参数化相同
     2) __functionName匹配被调用的函数名称;
     3) 用圆括号包含函数的形参,例如${__time(YMD)},不同函数要求的参数也不同;
     4) 有些JMeter函数不要求参数,则可以不使用圆括号,例如${__threadNum};

函数的参数的注意事项:

​ 如果函数参数包含逗号,请确保使用“ \” 转义,否则JMeter会将其视为参数分隔符.

例如:$ {__ time(EEE \,d MMM yyyy)}

如果逗号未被转义 - 例如$ {__ javaScript(Math.max(2,5))}

将收到如下错误:

错误 -  jmeter.functions.JavaScript:处理Javascript时出错:[Math.max(2)
    参数列表后面的org.mozilla.javascript.EvaluatorException:missing)(<cmd>
函数助手对话框

作用: 对Jmeter中自带函数的参数设置和生成使用.

启动方式:
       1) 菜单-选项->函数助手对话框
       2) Ctrl+Shift+F1
       3) 工具栏倒数第二个记事本图标

常用的函数

1. __CSVRead
2. __counter 
3. __Random
4. __time
5. __setProperty
6. __property
以上为我们使用Jmeter测试项目时常用的函数,接下来我们将逐个学习;
_CSVRead函数

作用: csvRead函数是从外部读取参数,可以从一个文件中读取多个参数

案例1

通过CSVRead函数读取csv.txt内容,作为请求时接口所需参数的值使用

http://127.0.0.1:8000/api/departments/?$dep_id_list=${__CSVRead(E:\jmeterScript\参数\csv.txt,0)}
,${__CSVRead(E:\jmeterScript\参数\csv.txt,1)}

csv.txt内容
    T01,T02
    T03,T04
    T05,T06
实现分析
1. 测试计划->线程组
2. 线程组->HTTP请求
3. 函数助手对话框->__CSVRead
4. 测试计划->监听器->察看结果树
参数配置

1. 选择一个功能:选择__CSVRead
2. CSV file to get values from | *alias:要读取的文件路径-(绝对路径)
3. CSV文件列号| next| *alias:从第几列开始读取,注意第一列是0
4. 点击生成函数引用格式
5. 全选复制
总结

流程与 CSV Data Set Config 类似,功能也类似,但是:

1、路径使用绝对路径,可移植性差

2、调用语法冗余(一次只能读取一个字段)

3、使用线程数控制执行次数,比较耗费资源

4、乱码处理不统一(不能使用 UTF-8,使用 ANSI)

综上: CSVRead 函数使用慎重(CSVRead 可以实现的, CSV Data Set Config 也可以)

 _counter 函数
作用:这个函数是一个计数器,用于统计函数的使用次数
 案例2

使用counter计次函数,请求(查询学院-所有)10次,为每次请求做计次标识  

http://127.0.0.1:8000/api/departments/?num=${__counter(FALSE,)}
实现分析
1. 测试计划->线程组
2. 线程组->HTTP请求
3. 函数助手对话框->__counter
4. 测试计划->监听器->察看结果树
参数配置 

参数解释:
 - true:代表每个用户有自己的计数器,可以用于统计每个线程各执行了多少次
 - false: 代表全局计数器,可以统计出这次测试运行了多少次
总结

1. __counter函数作用
2. __counter函数与指定值配合使用 num=${__counter(FALSE,)}
_Random函数

作用: 随机生成一个指定范围内的数

案例3
使用__Random函数对请求(查询学院-指定)进行随机查询

http://127.0.0.1:8000/api/departments/T0${__Random(1,6,)}/
实现分析

1. 测试计划->线程组
2. 线程组->HTTP请求
3. 函数助手对话框->__Random
4. 测试计划->监听器->察看结果树
函数配置图

1. 一个范围内的最小值:随机数生成时开始数,也是最小数
2. 一个范围内允许的最大值:随机生成结束数,也是最大数
 总结
1. __Random作用
2. __Random设置
_time 函数
作用:获取当前电脑日期时间 比如:MMddhhmmss 月/日/时分秒
案例4

使用__time函数解决迭代(学院-新增)dep_id重复问题

JSON报文:
        {
            "data": [
                        {
                            "dep_id":"time${__time(MMddhhmmss,)}",
                            "dep_name":"Test学院",
                            "master_name":"Test-Master",
                            "slogan":"Here is Slogan"
                        }
                      ]
        }
 实现分析

1. 测试计划->HTTP信息头管理器
2. 测试计划->线程组
3. 线程组->HTTP请求
4. 函数助手对话框->__time函数
5. 测试计划->监听器->察看结果树
_time函数参数配置

Format string for SimpleDateFormat (optional):
    1) 为空:返回毫秒
    2) 设置格式:MMddhhmmss 月/日/时/分/秒
 总结

1. __time作用
2. __time参数设置
 _setProperty 、__property 函数
作用:
setproperty作用: 用于把指定数据设定成Jmeter属性,可以理解为是把指定数据设置成全局变量 
property: 获取Jmeter属性值
案例5 
通过__setProperty函数把新增的学院ID,设置成全局变量
 实现分析

1. 测试计划-HTTP信息头管理器
2. 测试计划->JDBC Connection Configuration
3. 测试计划->线程组(学院-新增)
4. 线程组->HTTP请求(HTTP请求-学院新增)
5. 线程组->JDBC Request
6. 函数助手对话框->__setProperty函数
7. 线程组->BeanShell Sampler
8. 线程组->Debug Sampler
9. 测试计划->察看结果树
10. 工作台->Property Display

技术难点: 

1. __setProperty函数参数设置
2. BeanShell Sampler作用 
3. JDBC Request SQL语句
4. 如何知道设置的属性(全局变量)成功了
5._property函数获取参数值
 setproperty函数配置图

1. 属性名称:要设置的属性名称也就是全局变量名
2. Value of property:属性的值(全局变量的值)
 BeanShell Sampler 配置图

作用: 可执行js的一种一种容器

property函数配置图 

用来获取属性值

选项解析:
Name of variable in which to store the result (optional):获取属性的名称
总结

set_property把第一个请求响应的数据声明为属性,beanshell sampler 把属性提升为全局的变量,第二个线程组使用property获取声明为全局的变量值,从而达到第一个线程组的结果作为第二个线程组的参数的传递.

总结

每个函数都有不同的使用场景,重点我们要掌握的是SetProperty 和Property两种函数的使用.

Jmeter分布式

为什么使用Jmter分布式测试

需求

对学院接口(查询学院-所有)进行1000用户并发访问,测试服务器处理批量请求能力

现状

我们单台电脑由于配置(CPU、内存)问题,最模拟500用户时,就出现卡死现象

按照一般的压力机配置,jmeter的GUI模式下(Windows),最多支持300左右的模拟请求线程**,再大的话,容易造成卡顿、无响应等情况,这是限于jmeter其本身的机制和硬件配置。

有时候为了尽量模拟业务场景,需要模拟大量的并发请求,这个时候单台压力机就显得有心无力。针对这个情况,jmeter的解决方案是支持分布式压测,即将大量的模拟并发分配给多台压力机,来满足这种大流量的并发请求场景。

分布式测试

分布式测试原理

1. 一台电脑作为控制机(Controller),其它电脑做为执行机(Agent);
2. 执行时,控制机会把脚本发送到每台执行机上,执行机拿到脚本后就开始执行
3. 执行机执行时不需要启动Jmeter界面,可以理解它是通过命令行模式执行的
4. 执行完成后,执行机会把结果回传给控制机,控制机会收集所有执行机的信息并汇总

执行方案分析

1. 1台电脑(控制机)分发执行任务
2. 2台电脑(执行机)执行任务
3. 在执行机上启动监听服务程序
4. 在控制机上启动运行
5. 测试计划->聚合报告

 技术难点分析

1. 执行机-jmeter.properties设置
2. 控制机jmeter.properties设置
3. 执行机启动分布式监听服务程序
3. 2台执行机,用户数如何设置

执行机Jmeter.properties配置图

1. 分别打开执行机下jmeter安装文件下的bin目录:jmeter.properties, 找到server_port
2. server_Port=1099.    1099为执行机自定义端口号
3. 注意:
    - 要取消注释符号 #
    - 自定义的端口号,要选择未被占用的端口号

控制机jmeter.properties配置图

1.打开控制机下jmeter安装文件下的bin目录:jmeter.properties,找搭配remote_hosts
2. remote_hosts=127.0.0.1:1099,127.0.0.1:1100
3. 注意事项:
   - 多个执行机的ip间要用全英文的半角逗号隔开

执行机启动分布式监听效果图

1. 运行bin目录下jmeter-server.bat
2. 运行后勿关闭黑色窗口

控制机-线程数设置

注意:

需求1000用户,两台执行机同时接受控制机脚本去执行然后反馈给控制机,所以线程数设置500

控制机-运行效果图

1. 菜单-运行->选择全部启动
2. 快捷键->Ctrl+Shift+R

提示

1. 修改完端口要重启Jmeter.bat
2. 控制机和执行机分开(为了方便教学,这里采用控制机和代理机在1台机器上)
   1). 由于控制机需要发送信息给执行机并且会接受执行机回传的测试数据所以控制机自身会有消耗
3. 参数文件:如果使用csv进行参数化,那么需要把参数文件在每台执行机上拷一份且路径需要设置成一样的;
4. 每台机器上安装的Jmeter版本和插件最好都一致,否则会出一些意外的问题;
5. 执行机上Jmeter也许需要配置JDK环境变量;

分布式-总结

1. 分布式概念
2. 分布式原理
3. 执行机配置文件设置
4. 控制机配置文件设置
5. 控制机线程数设置

Jmeter 逻辑控制器

为什么要学习逻辑控制器?

需求

1. name=['百度','京东','淘宝']
2. name=百度,线程组执行请求百度;name=京东,线程组执行请求京东;name=淘宝,线程组执行请求淘宝;

问题

Jmeter目前学习的知识无法使用判断语句去控制HTTP取样器

什么是逻辑控制器?

概念:Jmeter逻辑控制器是可以控制采样器(Sampler)的执行顺序,它由多个逻辑控制语句封装成不同功能的组件组成

逻辑控制器作用域

作用域:只对其子节点的sampler有效。

逻辑控制器分类

1. 控制类:控制采样器执行顺序类
2. 分组类:对采样器进行分组类

常用(控制类)逻辑控制器

1. 如果(If)控制器
2. ForEach控制器
3. 交替控制器
4. 循环控制器
5. 仅一次控制器
6. 随机控制器
7. 随机顺序控制器
8. While Controller  
9. Switch Controller

如果(If)控制器

作用:条件成真,则执行控制器下所有取样器
解决方案分析
1. 测试计划->线程组
1. 测试计划->用户自定义变量
2. 线程组->如果(If)控制器
3. 如果(If)控制器->HTTP请求
4. 测试计划-察看结果树
技术难点分析
如果(If)控制器 参数设置
如果(If)控制器参数配置图

1. 条件:"${name}"=="百度"
2. 注意:
    1) 引用变量格式${name}需要被双引号括起来
    2) 两个等号
    3) 值需要被双引号括起来
如果(If)控制器-总结
1. 作用
2. 作用域
3. 条件格式

ForEach控制器

用:ForEach控制器一般和用户定义的变量一起使用,在用户自定义变量中读取一系列相关的变量。
案例1
使用ForEach组件读取自定义变量,作为请求(查询学院-所有)的user变量值使用
操作分析
1. 测试计划->线程组
2. 线程组->用户定义的变量
3. 线程组->ForEach控制器
4. ForEach控制器->HTTP请求
5. 测试计划->察看结果树
技术难点分析
1. ForEach控制器参数设置
2. 用户定义的变量书写格式(前缀+_+数字)
ForEach控制器参数配置图

1. 输入前缀变量:输入要遍历变量前缀
2. Start index for loop(exclusive):遍历变量开始的索引(从0开始)
3. End index for loop(inclusive):遍历变量结束的索引(不包括结束索引)
4. 输出变量名称:定义要被引用的变量名称
ForEach控制器-总结 
1. 作用
2. 使用ForEach读取用户定义的变量时,变量格式
3. ForEach参数设置

交替控制器

作用:交替控制,该控制器包含的所有取样器,步骤交错执行在每个循环中;
案例2
使用交替控制器,运行脚本完成2次请求:
第一次请求(查询学院-所有)和(查询学院-指定)
第二次请求(查询学院-所有)和(查询学院-id_list)
操作步骤分析
1. 测试计划->线程组
2. 线程组->HTTP请求(查询学院-所有)
3. 线程组->交替控制器
4. 交替控制器->HTTP请求(查询学院-指定)
5. 交替控制器->HTTP请求(查询学院-id_list)
交替控制器使用图

交替控制器-总结
1. 作用
2. 使用方法

循环控制器

作用:指定循环控制器内取样器的执行次数
案例3
使用循环控制器执行(查询学院-所有)接口10次
操作步骤分析
1. 测试计划->线程组
2. 线程组->循环控制器
3. 循环控制器->HTTP请求(查询学院-所有)
4. 测试计划->察看结果树
 循环控制器配置图

1. 循环次数:要执行的次数
循环控制器-总结
1. 作用

仅一次控制器

作用:该控制器下取样器只执行1次;
案例4
基于案例3循环控制器中执行10次(查询学院-所有),执行1次(查询学院-指定)
操作分析
1. 循环控制器->仅一次控制器
2. 仅一次控制器->HTTP请求(查询学院-指定)
仅一次控制器使用图

随机控制器

作用:随机执行其下的所某个请求
案例5
线程组下有3个查询学院请求[所有、指定、条件],使用随机控制器随机执行其中1个请求
操作步骤
1. 测试计划->线程组
2. 线程组->逻辑控制器->随机控制器
3. 随机控制器->HTTP请求(查询学院-所有)
4. 随机控制器->HTTP请求(查询学院-指定)
5. 随机控制器->HTTP请求(查询学院-条件)
6. 测试计划->察看结果树
随机控制器使用图

随机顺序控制器

作用:对控制器下所有采样器,随机产生1个执行顺序,进行执行
案例6
线程组下有3个查询学院请求[所有、指定、条件],使用随机顺序控制器,随机产生一个执行顺序
操作步骤
1. 测试计划->线程组
2. 线程组->逻辑控制器->随机顺序控制器
3. 随机控制器->HTTP请求(查询学院-所有)
4. 随机控制器->HTTP请求(查询学院-指定)
5. 随机控制器->HTTP请求(查询学院-条件)
6. 测试计划->察看结果树
随机顺序控制器使用图

1. 作用(顺序)
2. 随机顺序控制器与随机控制器的区别(三个中选一个,三个随机排序)

While Controller

作用:循环执行控制器内所有采样器,直到条件为假时,停止循环
案例7
线程组下有3个查询学院请求[所有、指定、条件],使用While控制器无限循环执行下去。
操作步骤:
1. 测试计划->线程组
2. 线程组->逻辑控制器->While Controller
3. 随机控制器->HTTP请求(查询学院-所有)
4. 随机控制器->HTTP请求(查询学院-指定)
5. 随机控制器->HTTP请求(查询学院-条件)
6. 测试计划->察看结果树
While Controller控制器使用配置图

1. 为空:最后一个请求失败,停止循环;(如果不是最后一个请求执行失败,则继续循环)
2. LAST:
    1) 与为空相同之处:最后一个请求失败,停止循环;(如果不是最后一个请求执行失败,则继续循环)
    2) 与为空不同之处:测试计划在执行while控制器之前如果有请求失败,则不会执行while控制器
3. 表达式:表达式为false退出循环;
    例如:
        1) ${VAR}:当参数VAR的值被设置成false时退出循环
        2) ${__javascript(“${VAR}”==”User1”)}:当参数VAR的值部位User1时退出循环
While Controller-总结
1. 作用
2. 取值为空和LAST区别;

Switch Controller控制器

作用:通过给该控制器中的value赋值,来指定运行哪个取样器;

Switch Controller控制器

作用:通过给该控制器中的value赋值,来指定运行哪个取样器;
案例8
线程组下有3个查询学院请求[所有、指定、条件],使用Switch控制器控制执行指定请求。
操作步骤:
1. 测试计划->线程组
2. 线程组->逻辑控制器->Switch Controller
3. 随机控制器->HTTP请求(查询学院-所有)
4. 随机控制器->HTTP请求(查询学院-指定)
5. 随机控制器->HTTP请求(查询学院-条件)
6. 测试计划->察看结果树
Switch Controller控制器配置图

1. Switch Value:为采样器名称;执行采样器名称相应的请求
2. Switch Value:为空;默认为执行第一个
3. Switch Value:为索引;第一个为0
Switch Controller-总结
1. 作用
2. 取值

常用(分组类)

1. 简单控制器
2. 事务控制器

简单控制器

作用:只做分组使用,犹如打隔断;
备注:
    添加简单控制器修改(查询学院),把(查询学院-所有、查询学院-指定)接口存放到此控制器下

简单控制器使用图

事务控制器

作用:生成一个额外的采样器来测量其下测试元素的总体时间; 
      值得注意的是,这个时间包含该控制器范围内的所有处理时间,而不仅仅是采样器的;

操作步骤分析

1. 测试计划->线程组
2. 线程组->逻辑控制器->事务控制器
3. 事务控制器->HTPP请求(查询所有、查询指定)
4. 测试计划->监听器->聚合报告

事务控制器使用图

事务控制器-总结:

1. 概念
2. 如何查看事务取样时间

 逻辑控制器-总结

1. 如果(If)控制器
2. ForEach控制器
3. 交替控制器
4. 循环控制器
5. 仅一次控制器
6. 随机控制器
7. 随机顺序控制器
8. While Controller  
9. Switch Controller

Jmeter图形监视器扩展插件

为什么要下载图形监视器扩展插件?

需求

请求(查询学院-所有)接口1000次的时候如何在客户端Jmeter工具中查看服务器(CPU、内存、网络、磁盘)的使用信息

问题

1. Jmeter工具打开监听器,没有监控服务器资源的组件
2. 服务器也没有启动被监听资源的服务程序

图形监视器扩展插件有什么用?

作用:提供客户端与服务器交互时各种图形报告

 解决方案分析

1. 服务器:下载服务器端监听服务程序(ServerAgent-2.2.1)
2. 客户端:下载标准扩展插件(JMeterPlugins-Standard-1.4.0.zip)
3. 启动服务器监听器程序(ServerAgent-2.2.1.zip)
4. 客户端加载监听服务器组件图表(jp@gc - PerfMon Metrics Collector)

插件下载

客户端:标准插件下载地址:Download :: JMeter-Plugins.org

使用:
    1) 解压文件后,复制JMeterPlugins-Standard.jar文件
    2) 将JMeterPlugins-Standard.jar粘贴到Jmeter的Lib目录下面的ext目录
    3) 重启Jmeter

服务器:性能监控插件下载地址:GitHub - undera/perfmon-agent: Server metrics fetching agent, based on SIGAR

1. 解压到服务器任意目录

服务器端监听服务程序图

运行:
    1) Windows启动startAgent.bat
    2) Linux启动startAgent.sh

客户端监听图表插件图

1. Host/IP:服务器IP地址
2. Port:默认4444(4444为启动服务器监听服务程序时的端口号)
3. Metric to collect:选择要收集的指标(CPU、内存、网络、磁盘)

插件-总结

1. 服务器端下载监听服务程序
2. 客户端下载标准插件包
3. 标准插件包位置
4. 启动服务监听服务程序

API文档分析

目标

  • 学会看项目API文档
  • 学会根据API文档整理出接口清单
  • 学会对API文档中有不清楚的业务该如何在API接口清单内体现

为什么要分析API文档?

回顾

首先回顾下之前讲过的API文档由来和作用图

API文档由来:

API文档作用:

项目API文档下载

1. API文档解析:

1.1 href节点:
作用:href节:为资源的请求地址;-(请求资源集合时的请求地址要与**href相符**)  
      "href":" http://127.0.0.1:8000/api/departments/"
1.2 items节点:
作用:为服务器响应数据时返回的资源属性;
         (请求接口时,返回的资源属性要与items里的属性相符)
"items": [
              {
                "href": "/T01",
                "data": {
                            "dep_id": "T01",
                            "dep_name": "Test学院",
                            "master_name": "Test-Master",
                            "slogan": "Here is Slogan"
                        }
             }
         ]
1.3 Links节点:
作用:为在当下资源下能连接到的直属下一资源
    (比如:A资源直接包含或关联着B、C两个资源,如果访问到A资源时,Links应定为到B和C)   
"links": [
            {
            "rel":"classes",
            "href":"http://127.0.0.1:8000/api/departments/T01/classes/ ",
            "prompt": "班级集合"
            }
         ]
1.4 templates节点:
作用:为新增和更新资源时所需要的属性
      (新增和更新资源时所需要的JSON报文属性)
"templates": {
                "data": [
                          {
                            "dep_id": "T01",
                            "dep_name": "Test学院",
                            "master_name": "Test-Master",
                            "slogan": "Here is Slogan"
                          }
                        ]
              }
1.5 queries节点:
作用:为资源支持的查询列表参数名和查询条件
      (资源支持哪些属性可以list查询;支持做查询条件的属性)   
"queries": [
             {
                "rel": "search",
                "href":" http://127.0.0.1:8000/api/departments/",
                "prompt": "列表查询",
                "data": [
                            {
                                "name": "$dep_id_list",
                                "value": ""
                            },
                            {
                                 "name": “$dep_name_list”,
                                 "value": ""
                             },
                             {
                                "name": "$master_name_list",
                                "value": ""
                              },
                              {
                                "name": "$slogan_list",
                                "value": ""
                              }
                        ]
                },
                   {
                    "rel": "condition",
                    "href":" http://127.0.0.1:8000/api/departments/",
                    "prompt": "条件查询(名称、简称)",
                    "data": [
                                {
                                    "name": " dep_name",
                                    "value": ""
                                },
                                {
                                    "name": " master_name",
                                    "value": ""
                                },
                                {
                                    "name": " slogan",
                                    "value": ""
                                 }
                              ]   
                }
            ]
1). 总结list列表查询
调用格式如:href/?$def_if_list=id1,id2
1. $dep_id_list
2. $dep_name_list
3. $master_name_list
4. $slogan_list
2). 总结条件查询
调用格式如:href/?dep_name=name&master_name=masterName&slogan=slogan
dep_name
master_name
slogan   
(注:查询条件支持单个使用和组合使用)

1.6 error节点作用说明:
出现错误时,返回的报文格式和属性
(如果请求资源有误,在响应数据时应在error节内体现)   
"error": {
            "status_code": "",
            "detail": ""
         }

接口清单整理:

一、 接口清单(学院)示例:
一、查询
    1.1学院-查询所有
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/
    1.2学院-查询指定
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/T02/
        (注:1为学院ID;)
    1.3学院-List-$dep_id_list查询
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/?$dep_id_list=T01,T02,T03 
        (注:$dep_id_list:为参数名称;T01,T02,T03为:学院ID;)
    1.4学院-List-$master_name_list查询
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/?$master_name_list=Java-Master,Test-Master
        (注:$master_name_list:为参数名称;a,b,c为:院长名称;)
    1.5学院-List-$slogan_list查询
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/?$slogan_list=Here is Slogan
        (注:$slogan_list:为参数名称;a,b,c为:学院口号;)
    1.6学院-List-$dep_name_list
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/?$dep_name_list=Test学院,Java2学院
        (注:$dep_name_list为参数名称;Test学院为:学院名称;)
    1.7学院-模糊 -BUG
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/?blur=1&dep_name=C
        (注:blur:为开启模糊查询参数1为开启;name:为参数名称;C:学院名称包含字符;)
    1.8学院-组合
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/?slogan=Here is Slogan&master_name=Test-Master&dep_name=Test学院
        (注:dep_name:学院名称;master_name:为院长名称;slogan:学院口号;三个条件可随意组合或单独使用)

        1.以上方法是否有遗漏?
        答:

        2.以上请求资源格式和参数格式是否正确?
        答:
二、新增
    2.1学院-新增
        1) 请求方法:POST
        2) 请求地址:http://127.0.0.1:8000/api/departments/
        3) 请求JOSN报文:
        4) 调用传入的json串如下(可新增多条,之间用,隔开):
            {
                "data": [
                        {
                            "dep_id": "T01",
                            "dep_name": "Test学院",
                            "master_name": "Test-Master",
                            "slogan": "Here is Slogan"
                        }
                  ]
            }

        1.以上请求新增资源URI是否正确?
        答:

        2.以上JSON报文格式是否正确?
        答:
三、更新
    3.1学院-更新
    1). 请求方法:PUT
    2). 请求地址:http://127.0.0.1:8000/api/departments/T03/
        (注:1:为学院ID)
    3). 请求JOSN报文:
            {
                "data": [
                        {
                            "dep_id": "T03",
                            "dep_name": "C++/学院",
                            "master_name": "C++-Master",
                            "slogan": "Here is Slogan"
                        }
                  ]
            }

    1.以上更新资源URI是否正确?
    答:

    2.以上更新JSON报文格式和属性是否正确?
    答:

四、删除
    4.1学院-删除单个
    请求方法:DELETE
    请求地址:http://127.0.0.1:8000/api/departments/T03/
    (注:10为学院ID)
    4.2学院-删除多个
    请求方法:DELETE
    请求地址:http://127.0.0.1:8000/api/departments/?$dep_id_list=8,9,11
    (注:$dep_id_list:为参数名称;8,9,11:为学院ID)

    1.以上删除资源URI是否正确?
    答:

    2.以上删除方法是否有遗漏?
二、接口清单(班级)示例:
一、查询
    1.1班级-查询所有
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/T01/classes/
        (注:T01:为班级ID)
    1.2班级-查询指定
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/T01/classes/2017T01C02/
        (注:TO1:为班级ID;2017T01C02为班级ID;)
    1.3班级-List-$cls_id_list查询
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/T01/classes/?$cls_id_list=2017T01C03,2017T01C04
        (注:T01:为班级ID;$cls_id_list:为参数名称;2017T01C03,2017T01C04为:班级ID;)
    1.4班级-List-$master_name_list查询
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/T02/classes/?$master_name_list=Master15,Master16
        (注:T02:为班级ID;$master_name_list:为参数名称;Master15,Master16为:班主任名称;)
    1.5班级-List-$cls_name_list查询
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/T01/classes/?$cls_name_list=2017级Test学院T01班,2017级Test学院T02班
        (注:T01:为班级ID;$cls_name_list:为参数名称;2017级Test班级T01班,2017级Test班级T02班为:班级名称;)
    1.6班级-List-$slogan_list查询
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/T02/classes/?$slogan_list=slogan15,slogan16
        (注:T02:为班级ID;$slogan_list:为参数名称;slogan15,slogan16为:班级口号;)
    1.7班级-组合
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/T01/classes/?cls_name=2018级Test学院T01班&master_name=Master&slogan=sloganSlogan&dep_name=Test班级
        (注:T01:学院ID;cls_name,master_name,dep_name:为参数名称;)

        1.以上方法是否有遗漏?
        答:

        2.以上请求资源格式和参数格式是否正确?
        答:
二、新增
    2.1班级-新增
        1) 请求方法:POST
        2) 请求地址:http://127.0.0.1:8000/api/departments/T02/classes/
                    (T02:为学院ID)
        3) 请求JOSN报文:
        4) 调用传入的json串如下(可新增多条,之间用,隔开):
             {
                "data": [
                            {
                                "cls_id": "2018T01C015",
                                "cls_name": "2018级Test学院T10班",
                                "master_name": "Master15",
                                "slogan": "slogan15"
                            }
                        ]
              }


        1.以上请求新增资源URI是否正确?
        答:

        2.以上JSON报文格式是否正确?
        答:
三、更新
    3.1班级-更新
    1). 请求方法:PUT
    2). 请求地址:http://127.0.0.1:8000/api/departments/T01/classes/2017T01C05/
        (注:T01:学院ID;2017T01C05:为班级ID)
    3). 请求JOSN报文:
             {
                "data": [
                            {
                              "cls_id":"2017T01C05",
                              "dep_id":"T01",
                              "cls_name":"2018级Test学院T01班",
                              "master_name":"Master",
                              "slogan":"slogan"
                            }
                        ]
              }


    1.以上更新资源URI是否正确?
    答:

    2.以上更新JSON报文格式和属性是否正确?
    答:

四、删除
    4.1班级-删除单个
    请求方法:DELETE
    请求地址:http://127.0.0.1:8000/api/departments/T01/classes/2018T01C08/
    (注:T01:为学院ID;2018T01C08:为班级ID)
    4.2班级-删除多个
    请求方法:DELETE
    请求地址:http://127.0.0.1:8000/api/departments/T01/classes/?$cls_id_list=2018T01C010,2018T01C011
    (注:T01:学院Id;cls_id_list:为参数名称;2018T01C010,2018T01C011:为班级ID)

    1.以上删除资源URI是否正确?
    答:

    2.以上删除方法是否有遗漏?
三、 接口清单(学生)示例:
一、查询
    1.1学生-查询所有
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/T01/classes/2017T01C01/students/
    1.2学生-查询指定
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/T01/classes/2017T01C01/students/2017T01C01001/
        (注:TO1:学院ID;2017T01C01:班级Id;2017T01C01001:学生id)
    1.3学生-List-$stu_id_list查询
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/T01/classes/2017T01C01/students/?$stu_id_list=2017T01C01001,2017T01C010013
        (注:TO1:学院ID;2017T01C01:班级Id;$stu_id_list:参数名称;2017T01C01001,2017T01C010013:学生id)
    1.4学生-List-$stu_name_list查询
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/T01/classes/2017T01C01/students/?$stu_name_list=张三,李四
        (注:TO1:学院ID;2017T01C01:班级Id;$stu_name_list:参数名称;张三,李四:学生姓名)
    1.5学生-条件
        请求方法:GET
        请求地址:http://127.0.0.1:8000/api/departments/T01/classes/2017T01C01/students/?stu_name=学生姓名
        (注:TO1:学院ID;2017T01C01:班级Id;stu_name:参数名称;学生姓名:参数值(学生姓名))

        1.以上方法是否有遗漏?
        答:

        2.以上请求资源格式和参数格式是否正确?
        答:
二、新增
    2.1学生-新增
        1) 请求方法:POST
        2) 请求地址:http://127.0.0.1:8000/api/departments/T01/classes/2017T01C01/students/
                    (注:TO1:学院ID;2017T01C01:班级Id;)
        3) 请求JOSN报文:
        4) 调用传入的json串如下(可新增多条,之间用,隔开):
            {
                "data": [
                        {
                               "stu_id": "2017T01C01002",
                               "stu_name": "王二New",
                               "gender": "0",
                               "birthday": "2018-01-01",
                               "native": "北京",
                               "phone_number": "",
                               "address": "",
                               "zipcode": "",
                               "email": "",
                               "note": ""
                         }

                  ]
            }

        1.以上请求新增资源URI是否正确?
        答:

        2.以上JSON报文格式是否正确?
        答:
三、更新
    3.1学生-更新
    1). 请求方法:PUT
    2). 请求地址:http://127.0.0.1:8000/api/departments/T01/classes/2017T01C01/students/2017T01C01001/
                    (注:TO1:学院ID;2017T01C01:为班级Id;班级Id;2017T01C01001:学生id)
    3). 请求JOSN报文:
            {
                "data": [
                        {
                          "stu_id":"2017T01C01001",
                          "dep_id":"T01",
                          "cls_id":"2017T01C01",
                          "stu_name":"王二",
                          "gender":false,
                          "birthday":"2018-01-01",
                          "native":"北京",
                          "phone_number":"",
                          "address":"",
                          "zipcode":"",
                          "email":"",
                          "note":""
                        }
                  ]
            }

    1.以上更新资源URI是否正确?
    答:

    2.以上更新JSON报文格式和属性是否正确?
    答:

四、删除
    4.1学生-删除单个
    请求方法:DELETE
    请求地址:http://127.0.0.1:8000/api/departments/T01/classes/2017T01C01/students/2017T01C010016/
                (注:TO1:学院ID;2017T01C01:为班级Id;2017T01C010016:学生id)

    1.以上删除资源URI是否正确?
    答:

    2.以上删除方法是否有遗漏?

API文档

目标

文档内浏览项目API文档


API- Meta Model
版本 1.0
2018-1-11

修订记录

修订日期章节索引修订摘要提出人修订人
1.    服务版本
API- Meta Model 1.0
2.    项目说明
学生信息管理系统的功能是收集学生的个人信息,以便向老师提供每个学生在校或毕业生学籍的情况,还可以让学生用自己的学号去查看自己在校期间的表现。
2.1    学院信息(Department)及其集合(Departments)
描述
    学院信息集合

属性

属性属性描述是否必填备注
dep_id学院编号,主键新增时必填
dep_name学院名称最长20位
master_name院长名称最长20位
Slogan口号最长100位
资源描述
{
  "collection": {
"version": "1.0",
"href":" http://127.0.0.1:8000/api/departments/",
"links": [ ],
"items": [
      {
        "href": "/T01",
        "data": {
            "dep_id": "T01",
            "dep_name": "Test学院",
            "master_name": "Test-Master",
            "slogan": "Here is Slogan"
        },
"links": [
{
"rel":"classes",
"href":"http://127.0.0.1:8000/api/departments/T01/classes/ ",
"prompt": "班级集合"
}
                ]
            }
       ],
        "templates": {
"data": [
          {
            "dep_id": "T01",
            "dep_name": "Test学院",
            "master_name": "Test-Master",
            "slogan": "Here is Slogan"
        }
  ]
 },
"queries": [
      {
"rel": "search",
"href":" http://127.0.0.1:8000/api/departments/",
"prompt": "列表查询",
"data": [
{
"name": "$dep_id_list",
"value": ""
},{
                  "name": “$dep_name_list”,
                  "value": ""
      },{
"name": "$master_name_list",
"value": ""
          },{
"name": "$slogan_list",
"value": ""
  }
]
},{
"rel": "condition",
"href":" http://127.0.0.1:8000/api/departments/",
"prompt": "条件查询(名称、简称)",
"data": [
{
"name": " dep_name",
"value": ""
},{
"name": " master_name",
"value": ""
},{
"name": " slogan",
"value": ""
  }
       ]   
}
],
"error": {
"status_code": "",
"detail": ""
    }
  }
}

Queries释义

查询方法参数规则值规则
列表集合查询$字段名_list,如”$dep_id_list”使用英文逗号分隔多个值
单查询字段名称(除主键外),如”dep_name”
模糊查询Blur默认不开启模糊查询,blur=1将开启模糊查询
2.2    班级信息(Class)及其集合(Classes)
描述
    学院下的班级信息集合

属性

属性属性描述是否必填备注
cls_id班级编号,主键新增时必填
cls_name班级名称最长20位
master_name班主任名称最长20位
slogan口号最长100位
dep_id所属学院编号
资源描述
    {
  "collection": {
"version": "1.0",
"href":" http://127.0.0.1:8000/api/departments/T01/classes/",
"links": [ ],
"items": [
      {
        "href": "/2017T01C01",
        "data": {
            "cls_id": "2017T01C01",
            "dep_id": "T01",
            "cls_name": "2017级Test学院C01班",
            "master_name": "Master",
            "slogan": "slogan"
        },
"links": [
{
"rel":"students",
"href":"http://127.0.0.1:8000/api/departments/T01/classes/2017T01C01/students/ ",
"prompt": "学生集合"
}
                ]
            }
       ],
        "templates": {
"data": [
           {
            "cls_id": "2017T01C01",
            "cls_name": "2017级Test学院T01班",
            "master_name": "Master",
            "slogan": "slogan"
   }
  ]
 },
"queries": [
      {
"rel": "search",
"href":"http://127.0.0.1:8000/api/departments/T01/classes/",
"prompt": "列表查询",
"data": [
{
"name": "$cls_id_list",
"value": ""
},{
                  "name": “$cls_name_list”,
                  "value": ""
      },{
"name": "$master_name_list",
"value": ""
          },{
"name": "$slogan_list",
"value": ""
            }
]
},{
"rel": "condition",
"href":"http://127.0.0.1:8000/api/departments/T01/classes/",
"prompt": "条件查询(名称、简称)",
"data": [
{
"name": " cls_name",
"value": ""
},{
"name": " master_name",
"value": ""
},{
"name": " slogan",
"value": ""
  }
       ]   
}
],
"error": {
"status_code": "",
"detail": ""
    }
  }
}

Queries释义

查询方法参数规则值规则
列表集合查询$字段名_list,如”$cls_id_list”使用英文逗号分隔多个值
单查询字段名称(除主键外),如”cls_name”
2.3    学生个人信息(Student)及其集合(Students)
描述
    学生个人信息集合

属性

属性属性描述是否必填备注
stu_id学号、主键新增时必填
stu_name学生姓名最长20位
gender性别值只能是0\1\false\true
birthday出生日期YYYYMMDD\YYYY-MM-DD
native籍贯最长20位
cls_id所属班级id、外键新增时必填
dep_id所属学院id、外键新增时必填
phone_number联系方式最长100位
address住址最长20位
zipcode邮编最长8位
email邮箱最长20位
note备注说明最长200位
资源描述
    {
  "collection": {
"version": "1.0",
"href":"http://127.0.0.1:8000/api/departments/T01/classes/2017T01C01/students/",
"links": [ ],
"items": [
      {
        "href": "/2017T01C01001",
        "data": {
            "stu_id": "2017T01C01001",
            "dep_id": "T01",
            "cls_id": "C01",
            "stu_name": "学生姓名",
            "gender": "男",
            "birthday": "2018-01-01",
            "native": "北京",
            "phone_number": "",
            "address": "",
            "zipcode": "",
            "email": "",
            "note": ""
        },
"links": []
       ],
        "templates": {
"data": [
          {

            "dep_id": "T01",
            "cls_id": "C01",
            "stu_id": "2017T01C01001",
            "stu_name": "学生姓名",
            "gender": "男",
            "birthday": "2018-01-01",
            "native": "北京",
            "phone_number": "",
            "address": "",
            "zipcode": "",
            "email": "",
            "note": ""
 }
  ]
 },
"queries": [
      {
"rel": "search",
"href":"http://127.0.0.1:8000/api/departments/T01/classes/2017T01C01/students/",
"prompt": "列表查询",
"data": [
{
"name": "$stu_id_list",
"value": ""
},{
                  "name": “$stu_name_list”,
                  "value": ""
      }
]
},{
"rel": "condition",
"href":"http://127.0.0.1:8000/api/departments/T01/classes/2017T01C01/students/",
"prompt": "条件查询(名称、简称)",
"data": [
{
"name": " stu_name",
"value": ""
}
       ]   
}
],
"error": {
"status_code": "",
"detail": ""
    }
  }
}

Queries释义

查询方法参数规则值规则
列表集合查询$字段名_list,如”$stu_id_list”使用英文逗号分隔多个值
单查询字段名称(除主键外),如”stu_name”

项目功能脚本设计

目标

基于Jmeter设计学生信息管理系统-接口功能脚本


1. 配置元件分析

1. HTTP信息头管理器
2. HTTP请求默认值
3. CSV Data Set Config

2. 请求方法

1. 查询使用方法(GET)
2. 新增使用方法(POST)
3. 更新使用方法(PUT)
4. 删除使用方法(DELETE)

3. 其他

1. 参数文件使用Nodepad++ UTF-8无BOM格式
   2. 每个接口为一个线程组,例如:(查询所有、查询指定、新增、更新、删除)

4. 配置示例图

4.1 信息头管理器设置图:

4.2 HTTP请求默认值设置图:

4.3 CSV Data Set Config设置图

4.4 HTTP请求设置图:

4.5 nodepad++ UTF-8无BOM格式

4.6 功能脚本示例图:

项目接口用例设计

目标

  • 掌握接口用例设计技巧
  • 理解接口用例设计时划分颗粒度

1. 用例设计原则

1. 覆盖所有的必选参数
2. 组合可选参数
3. 参数边界值
4. 如果参数的取值范围是枚举变量,需要覆盖所有枚举值    
5. 空数据
6. 包含特殊的字符
7. 越界的数据
8. 错误的数据

2. 用例格式

用例编号用例名称方法测试步骤预期结果是否通过备注

3. 用例实现示例:

3.1 查询

查询指定-正向
1. 传入参数(格式正确且存在)
2. 传入参数(格式正确)
查询-逆向
1. 校验-为空
2. 校验-格式不正确
查询List 正向
1. list参数全部存在且格式正确
2. list有一个以上存在且格式正确
查询List 逆向
1. list参数都为空
2. list都不存在
查询-组合 正向
1. 传入全部条件存在且格式正确  
2. 单个条件或部分条件组合(存在且格式正确)
查询-组合 逆向
1. 全部为空
2. 一个以上不存在或格式不正确
查询-模糊 正向
1. 传入模糊参数或全部参数

3.2 新增

新增正向
1. 传入新增全部参数,格式正确
2. 传入新增必填参数,其他为空
新增逆向
1. 传入已存在id,唯一不重复字段校验
2. 类型校验(时间日期、布尔、指定范围值0/1) 
3. 格式不正确校验

3.3 更新

更新正向
1. 传入更新全部参数,格式正确
2. 软入更新必填参数,其他为空
更新逆向
1. 传入id不存在
2. 类型校验(时间日期、布尔、指定范围值0/1) 
3. 格式不正确校验

3.4 删除

删除正向-指定
1. 传入正确ID且存在
删除逆向-指定
1. 传入ID不正确
删除正向-list
1. 传入全部正确id
2. 传入部分正确id
删除逆向
1. 传入全部不存在id
2. 传入id为空

4. 接口用例效果图

项目用例中测试数据参数化

目标

  • 给测试用例设计测试数据

测试数据分类

  • 正向:(根据测试用例覆盖面设计正向测试数据)
  • 逆向: (根据测试用例覆盖面设计逆向测试数据)

正向方法:

1. 传入所有可传属性,且格式正确
2. 传入必填属性,且格式正确

校验方法:

1) 为空校验
2) 为空格校验
3) 前后含空格校验
4) 超长校验
5) 类型校验
6) 含特殊字符校验
7) 删除引用校验
8) 唯一不重复属性校验

参数化示例:

学院-组合查询参数化示例:

Here is Slogan,Test-Master,Test学院,----正向数据
,Test-Master,Test学院,----两个条件组合
,,Test学院,----单个条件
Here is Slogan,,,----正向数据
,Test-Master,,----正向数据
 Here is Slogan ,Test-Master,Test学院,----Here is Slogan前后含空格
Here is Slogan, Test-Master ,Test学院,----Test-Master前后含空格
Here is Slogan,Test-Master, Test学院 ,----Test学院前后含空格
 ,Test-Master,Test学院,----为空格
Here is Slogan, ,Test学院,----为空格
Here is Slogan,Test-Master, ,----为空格
Here is Slogan222,Test-Master,Test学院,----一个条件不存在
Here is Slogan,Test-Master22222222222222222222222222222,Test学院,----超长
Here is Slogan,Test-Master,Test%%%%%%%%%%%%%院,----含特殊字符
,,,----全部为空

学院-list删除参数化示例:

T07,T08,---正确数据
,T08,---一个为空
T07,,---一个为空
T07,T0888,---一个为正常,一个为不存在
 ,T08,---一个为空格
T07, ,---一个为空格
 T07 , T08 ,---前后含空格 
T07%,T08,---一个为格式不正确
T07,T0888888888888888,---一个为超长

`

学院新增参数化示例:

T09,Test学院,Test-Master,Here is Slogan,-----正向数据
---ID存在校验
T09,Test学院,Test-Master,Here is Slogan,-----ID已存在
---为空校验
,Test学院,Test-Master,Here is Slogan,-----depid为空
T09,,Test-Master,Here is Slogan,-----depname为空
T09,Test学院,,Here is Slogan,-----mastername为空
T09,Test学院,Test-Master,,-----slogan为空
---为空格校验
 ,Test学院,Test-Master,Here is Slogan,-----depid为空格
T09, ,Test-Master,Here is Slogan,-----depname为空格
T09,Test学院, ,Here is Slogan,-----mastername为空格
T09,Test学院,Test-Master, ,-----slogan为空格
---含前后空格
 T09 ,Test学院,Test-Master,Here is Slogan,-----depid含前后空格
T09, Test学院 ,Test-Master,Here is Slogan,-----depname含前后空格
T09,Test学院, Test-Master ,Here is Slogan,-----mastername含前后空格
T09,Test学院,Test-Master, Here is Slogan ,-----slogan含前后空格
---含前后#
#T09#,Test学院,Test-Master,Here#is#Slogan,-----depid含前后#
T09,#Test学院#,Test-Master,Here#is#Slogan,-----depname含前后#
T09,Test学院,#Test-Master#,Here#is#Slogan,-----mastername含前后#
T09,Test学院,Test-Master,#Here#is#Slogan#,-----slogan含前后#
---含前后@
@T09@,Test学院,Test-Master,Here@is@Slogan,-----depid含前后@
T09,@Test学院@,Test-Master,Here@is@Slogan,-----depname含前后@
T09,Test学院,@Test-Master@,Here@is@Slogan,-----mastername含前后@
T09,Test学院,Test-Master,@Here@is@Slogan@,-----slogan含前后@
---含前后%
%T09%,Test学院,Test-Master,Here%is%Slogan,-----depid含前后%
T09,%Test学院%,Test-Master,Here%is%Slogan,-----depname含前后%
T09,Test学院,%Test-Master%,Here%is%Slogan,-----mastername含前后%
T09,Test学院,Test-Master,%Here%is%Slogan%,-----slogan含前后%
---超长校验
T091111111111111111111111122222222222222,Test学院,Test-Master,Here%is%Slogan,-----depid含超长
T09,Test学院222222222222222222222,Test-Master,HereisSlogan,-----depname含超长
T09,Test学院,%Test-Master1111111111111111111111,HereisSlogan,-----mastername超长
T09,Test学院,Test-Master,HereisSlogan11111111111111111111,-----slogan超长

接口自动化脚本设计

目标

  • 掌握自动化脚本设计原则;
  • 掌握自动化脚本调优技巧;

为什么要设计自动化脚本?

1. 后台程序更新/发布新版之前需要验证下之前的功能是否能用

1. 自动化脚本设计原则:

1. 每个测试用例都是独立的
2. 测试用例都是可重复运行的
3. 测试数据与业务相分离(测试数据包括参数接口数据/ 测试执行所需要的系统数据)
4. 测试完成后,要删除不必要的测试数据。

2. 自动化脚本设计组件分析:

1. 使用数据库连接池
2. 使用setUp Thread Group线程组
3. 使用tearDown Thread Group线程组
4. 使用线程组
5. 使用函数
6. 使用关联
7. 添加断言
8. 使用sampler中请求
9. 添加聚合报告

3. 组件配置图

3.1 测试计划:

勾选独立运行每个线程组(目的:保证线程组执行从上到下,保持先增后改再查再删原则)

3.2 HTTP请求:

使用函数,确保无论运行多少次,保证唯一属性不重复

3.3 响应断言:

使用函数,确保每次请求后响应数据达到预期结果

4.整体效果动态演示

5.脚本执行含查数据库演示

6. 自动化脚本-总结

1. 使用数据库连接池
2. 使用setUp Thread Group线程组
3. 使用tearDown Thread Group线程组
4. 使用线程组
5. 使用函数(setProperty、Property、counter)
6. 使用关联
7. 添加断言
8. 使用循环
9. 使用sampler中(HTTP请求、BeanShell Sampler、JDBC Request、Debug Sampler)请求
10. 去除单接口察看结果树
11. 添加聚合报告
12. 将测试计划中独立运行每线程组勾选

接口性能压测脚本设计

目标

- 性能脚本设计技巧

1. 为什么要设计性能脚本?

1.1 需求

100虚拟用户对(查询学院-所有)接口测试,以每秒启动10个用户,统计服务器平均响应时间和错误率

1.2 问题

100虚拟用户请求服务器的时候,如何统计服务器响应时间和错误率?

2. 什么是性能脚本?

概念:借助测试工具模拟多种业务需求操作对系统的各项性能指标进行测试的脚本

2.1 解决方案分析

1. 测试计划->线程组
2. 线程组->HTTP请求(查询学院-所有)
3. 测试计划->查看结果树
4. 测试计划->聚合报告

2.2 技术难点分析

1. 线程组-线程数
2. 测试计划-聚合报告(各指标意义)

2.3 聚合报告使用图

1. Label:在不勾选"Include group name in label?"复选框的情况下,为请求取样器的名称,
    否则为“请求取样器所在线程组:请求取样器名称”
2. Samples:用同一个请求取样器,发送请求的数量(注意:该值是不断累计的)。
    比如,10个线程数设置为10,迭代10次,那么每运行一次测试,该值就增加10*10=100
3. Average:请求的平均响应时间
4. Median:中位数。50%的样本都没有超过这个时间。
    这个值是指把所有数据按由小到大将其排列,就是排列在第50%的值。
5. 90% Line:90%的样本都没有超过这个时间。这个值是指把所有数据按由小到大将其排列,就是排列在第90%的值。
6. Min:针对同一请求取样器,请求样本的最小响应时间
7. Max:针对同一请求取样器,请求样本的最大响应时间
8. Error %:出现错误的请求样本的百分比
9. Throughput:吞吐量以“requests/second、requests /minute、requests /hour”来衡量。
    时间单位已经被选取为second,所以,显示速率至少是1.0,即每秒1个请求。
10. Received KB/sec - 收到的千字节每秒的吞吐量测试。
11. Kb/sec - 以Kilobytes/seond来衡量的吞吐量(发送的千字节每秒的吞吐量测试)

3. 项目-性能脚本设计

3.1 性能脚本设计技巧

1. 新增、更新、删除:使用固定数值,比如新增100条,更新100条,删除100条
2. 查询:采用虚拟用户数和持续时间配合(比如,100用户,持续查询600秒)

3.2 提示

1. 参数化:参数化尽量避免采用从外部读取参数,使用固定参数+函数形式( 如:${__counter(TRUE,)})
2. 察看结果树:必须清除单个接口内察看结果树
            (如不去掉,非常占用测试机自身性能),在测试计划下添加一个察看结果树  
3. 报告:性能报告可根据实际需求选择,建议保留添加聚合报告  
4. 线程组:增删改查每一个功能点,都需建立单独线程组,而避免在同一个线程组内添加
           多个HTTP请求完成增删改查(一便参数化对单个请求做压测和并发)
5. 分布式:如并发数量大,采用分布式测试
6. 新增/删除:新增和删除接口建议不要采用时间模式来压测,直接使用线程数和循环;

3.3 性能脚本设计-总结

1. 参数化
2. 报告
3. 查询接口-性能常用方式

生成HTML报告


目标

  • 学习如何利用Jmeter生成HTML报告

1. 效果

浏览ResultReport

2. Statistics 图

3. Response Time Overview 图

提示:

JMeter 3.0以上开始支持自动生成动态报告


操作步骤:

两种模式:

    1. 无jtl日志或csv日志文件生成报告
    1. 有jtl日志或csv日志文件生成报告

1. 无日志文件生成:

1.1 基本命令格式:

jmeter -n -t <test JMX file> -l <test log file> -e -o <Path to output folder>

1.2 样例: jmeter -n -t E:\课件\Jmeter\Script\自动化脚本\Stu_AutoScript.jmx -l testLog -e -o ./output/report

1.3 参数详解:

  • -n :以非GUI形式运行Jmeter

  • -t :source.jmx 脚本路径

  • -l :运行结果保存路径(.jtl),此文件

    必须不存在

      1) .jtl
      2) .txt
      3) 无后缀
    
  • -e :在脚本运行结束后生成html报告

  • -o :保存html报告的地址, 此文件必须不存在


2. 使用已有的jtl日志文件或csv日志文件生成

2.1 基本命令格式:

jmeter -g <log file> -o <Path to output folder>

2.2 样例:

jmeter -g E:\课件\Jmeter\Script\自动化脚本\resultt.jtl -o ./outputreport

提示:

  • 执行命令时,出现Jmeter不是内部或外部命令

解决:
  • 将Jmeter目录下bin文件目录添加到环境变量Path中,注意追加路径是,前面分号;

接口测试基于Jmeter项目汇总


目标

  • 总结接口基于Jmeter测试流程

1. API文档

1. 梳理业务需求整理接口清单
2. 提出相应不确定问题

2. 接口清单

1. 设计测试用例
2. 设计功能脚本

3. 测试用例

1. 设计参数化覆盖数据

4. 功能脚本

1. HTTP请求默认值(设置服务器IP或域名;Content encoding:UTF-8)
2. HTTPHTTP信息头管理器(ContentType:application/json;charset=utf-8)
3. 线程组
4. Sampler(如:HTTP请求)  
 5. 新增-方法(POST)
 6. 更新-方法(PUT)
7. 删除-方法(DELETE)
8. 查询-方法(GET)
9. 查看结果树

5. 自动化脚本

1. 测试计划:勾选(独立运行每个线程组)确保脚本执行顺序,从上
2. 如不校验逆向数据,请去除CSV Data Set Config
3. 添加断言
4. 去除每个接口内的察看结果树元件
5. 动态数据使用**关联**(正则表达式/XPath)获取
6. 脚本执行顺序(先增数据,拿到新增数据ID后;在更新完毕数据后,查询新增数据;最后删除新增数据)  
7. 基于测试计划添加聚合报告和察看结果树

6.性能测试脚本

1. 线程组:设置线程数,调度器;
2. 监听器:察看结果树/聚合报告/用表格察看结果/Aggregate Graph
3. 检测服务器性能:(jp@gc - PerfMon Metrics Collector和ServerAgent插件)

性能知识点-科普


目标

  • 理解Jmeter常用性能术语

1. 需求

测试100个用户访问 https://www.baidu.com在负载达到60 QPS时的平均响应时间。

1.1 问题

1. QPS是什么?
2. 如何每秒负载数达到60QPS?

2. QPS是什么?

概念:QPS:Query Per Second每秒查询率。是一台查询服务器每秒能够处理的查询次数。

2.1. 如何每秒达到负载数达到60QPS?

2.2 Constant Throughput Timer(常数吞吐量定时器)

作用:控制Jmeter中请求时指定的吞吐量,单位为分钟

2.3 解决方案分析

1. 测试计划->线程组
2. 线程组->HTTP请求
3. HTTP请求->Constant Throughput Timer
4. 测试计划->察看结果树
5. 测试计划->聚合报告

2.4 技术难点分析

1. Constant Throughput Timer 参数配置

2.5 Constant Throughput Timer配置图

1. Target throughput(in samples per minte):目标吞吐量。
  注意:
    1) 这里是每分钟发送的请求数,实际填的数值为:60*QPS(60*60);    

2. Calculate Throughput based on:  
    1) This thread only:控制每个线程的吞吐量,选择这种模式时;
        总的吞吐量为设置的target Throughput乘以该线程的数量。

2.5 QPS-总结:

1. QPS概念
2. Constant Throughput Timer

3. Jmeter常用性能术语[了解]

3.1 响应时间(RT)

概念:响应时间是指系统对请求作出响应的时间。  

备注:
    响应时间指用户从客户端发起一个请求开始,到客户端接收到从服务器端返回结果的响应结束;
    结果信息展现在客户端,整个过程所耗费的时间。

3.2 吞吐量(Throughput)

概念:单位时间内服务器处理的客户端请求数量

备注:
    1) 通常情况下,吞吐量用“请求数/秒”或者“页面数/秒”来衡量。
    2) 从业务角度来看,吞吐量也可以用“业务数/小时”、“业务数/天”、“访问人数/天”、“页面访问量/天”来衡量。
    3) 从网络角度来看,还可以用“字节数/小时”、“字节数/天”等来衡量网络的流量。

3.3 并发用户数

概念:多个用户同一时间操作同一业务。
备注:
  1) 并发用户数的时候一般结合集合点(Synchronizing Timer)一起使用;

Jmeter FTP服务器测试


目标

1. 了解基于Jmeter测试FTP服务器

1. FTP是什么?

概念:FTP是英语File Transfer Protocol的首字母缩写,汉语意思为文件传输协议。

2. FTP有什么用?

作用:可以根据FTP服务器进行上传和下载文件(上传、下载需要权限)

3. 如何测试FTP服务器?

3.1 操作步骤分析

1. 测试计划->线程组(线程组-下载)
2. 线程组->FTP请求(FTP请求-下载)
3. 测试计划->线程组(线程组-上传)
4. 线程组->FTP请求(FTP请求-上传)
5. 测试计划->监听器->用表格察看结果

3.2 FTP请求-下载配置图

1. Remote file:上传到FTP服务器新的文件名+后缀
2. local file:为本地存放到本机上的路径+文件名+后缀
3. get(RETR):为下载文件
4. 用户名:ftp服务器用户名
5. 密码:ftp服务器的密码

3.3 FTP请求-上传配置图

1. Remote file:上传到FTP服务器新的文件名+后缀
2. local file:为本地存放到本机上的路径+文件名+后缀
3. put(RETR):为上传文件
4. 用户名:ftp服务器用户名
5. 密码:ftp服务器的密码

3.4 用表格察看结果使用图

一种统计响应数据组件

3.4 FTP服务器测试-总结

1. FTP概念
2. FTP下载方法
3. FTP上传方法

Jmeter Test Fragment(测试片段)


目标

1. 了解Test Fragment(测试片段)使用场景和方法

1. Test Fragment(测试片段)

1.1 Test Fragment(测试片段)是什么?

概念:测试片段元素是控制器上的一个种特殊的线程组,它在测试树上与线程组处于一个层级。

备注:
    它与线程组有所不同,因为它不被执行,除非它是一个模块控制器或者是被控制器所引用时才会被执行。

1.2 Test Fragment(测试片段)有什么用?

作用:把单独功能脚本封装起来,待需要使用的时候,直接引用;类似于python中的函数或模块

2. 案例

使用Test Fragment把(查询学院-指定)接口进行封装,然后使用模块控制器调用

2.1 操作步骤分析

1. 测试计划->Test Fragment
2. Test Fragment->HTTP请求(查询学院-指定)
3. 测试计划->线程组
4. 线程组->逻辑控制器->模块控制器
5. 测试计划->察看结果树

2.2 Test Fragment使用图

1. Test Fragment默认为不启用状态

2.3 模块控制器使用图

Module To Run:选择要运行的封装片段

提示:测试片段被引用执行两种方式
    1) 模块控制器可以引用执行测试片段;
    2) Include Controller控制器也可以引用和执行片段;

2.4 Test Fragment-总结:

1. 作用
2. 测试片段引用执行两种方式

Jmeter 工作台-非测试元件


目标

1. 了解Jmeter工作台作用
2. 了解Jmeter工作台下非测试元件

1. 工作台

1.1 工作台有什么用?

作用:工作台为测试中不使用的测试元素提供一个临时存的位置;
目的:是提供复制/粘贴或者其他目的。

提示:保存测试计划工作台的内容默认不会保存除非选择”save workbench”(保存工作台)选项

1.2 非测试元件分类

1. HTTP Mirror Server
2. HTTP代理服务器
3. Property Display

2. HTTP Mirror Server

2.1 HTTP Mirror Server有什么用?

作用:它可以在本地临时搭建一个HTTP服务器,该服务器把接收到的请求原样返回;
      这样就可以看到发送出的请求的具体内容,以供调试;

2.2 HTTP Mirror Server配置使用图

1. 端口号:默认搭建临时HTTP服务器端口为8081,可自定义修改(未占用端口号);
2. 启动:点击启动,启动HTTP临时搭建的服务

2.3 HTTP请求使用HTTP Mirror搭建服务器图

2.4 HTTP Mirror Server-总结

1. HTTP Mirror Server作用
2. 使用方法

3. HTTP代理服务器

3.1 为什么要了解HTTP代理服务器?

3.2 需求

使用Jmeter测试http://www.so.com搜索引擎,搜索测试,搜索三个关键字Java
、python、Test]

3.3 问题

1. 如何使用Jmeter录制HTTP协议的Web程序脚本?

3.4 HTTP代理服务器是什么?

概念:HTTP代理服务器是监听浏览器代理,记录基于代理浏览器所有的请求,并以Jmeter可执行的脚本格式保存。

3.5 解决方案分析

1. 工作台->非测试元件->HTTP代理服务器
2. IE->工具->Internet选项->代理服务器设置
3. 测试计划->线程组

3.6 HTTP代理服务器配置图

1. 端口号:可自定义端口(未必占用的端口号)
2. 目标控制器:选择录制的脚本要保存的地方,比如:测试计划-线程组
3. 启动:启动监听程序

3.7 HTTP代理服务器-总结

1. HTTP代理服务器作用
2. HTTP代理服务器设置
3. IE代理设置

4. Property Display

作用:以GUI图形界面显示Jmeter配置文件(jmeter.properties)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人生如梦亦如幻唯心在

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值