前文回顾:性能测试中记录每一个耗时请求,做完了单接口耗时请求的记录功能,近期又迎来了一批多接口链路压测的需求。刚好趁着这个机会,多实现一些不同场景的链路压测需求,锻炼一波,也能提高自己写的FunTester测试框架的兼容性,可谓一石多鸟,何乐而不为。

非技术内容的分享简略一些。

业务需求

老师在首页看到资源列表后,对相应的列表进行收藏和取消收藏操作。

接口参数

  • 收藏
/**
*收藏OK智课
* @param minicourse_id
* @param ktype 0-机构,1-老师
* @return
*/
public JSONObject collect(int minicourse_id = 43089, int ktype = 0, int grade_id = 12) {
String url = OKClassApi.COLLECT
def params = getParams()
params.put("org_id", 80);
params.put("org_type", 1);
params.put("minicourse_id", minicourse_id);
params.put("kid_route", [82]);
params.put("ktype", ktype);
params.put("grade_id", grade_id);
params.put("link_source", 1);//0-教师空间,1-教师机
def response = getPostResponse(url, params)
output(response)
response
}
  • 取消收藏
/**
* 取消收藏
* @param minicourse_id
* @param ktype
* @param grade_id
* @return
*/
public JSONObject unCollect(int minicourse_id = 43089, int ktype = 0) {
String url = OKClassApi.UNCOLLECT
def params = getParams()
params.put("minicourse_id", minicourse_id);
params.put("kid_route", [82]);
params.put("ktype", ktype);
def response = getPostResponse(url, params)
output(response)
response
}

测试方案

通过创建不用的用户对象,一个线程绑定一个用户对象,使用该对象进行收藏取消收藏操作。把一次循环当做一个​​request​​​进行数据的统计,计算​​QPS​​​和​​RT​​​等数据,生成测试结果图像。此处参考:性能测试中图形化输出测试数据。

链路压测中如何记录每一个耗时的请求_json

测试脚本

测试脚本使用​​Groovy​​​,方便在服务器上执行,基本跟​​Java​​没有差别。

我用一个​​AtomicInteger​​​对象来控制每一个线程创建的用户对象不同,具体方法是​​ OkayBase okayBase = getBase(u.getAndIncrement())​​​。通过获取每个对象最后一次发出请求的​​HttpRequestBase​​​请求,获取请求的​​Mark​​​对象值​​requestid​​​,拼接到线程标记对象​​threadmark​​​中,这样就可以获取到耗时的请求了。具体的数据格式如下:​​1218_Fdev160809808115759_Fdev160809808182457​​。

package com.okayqa.composer.performance.master1_0

import com.fun.base.constaint.ThreadLimitTimesCount
import com.fun.frame.execute.Concurrent
import com.fun.frame.httpclient.ClientManage
import com.fun.utils.ArgsUtil
import com.okayqa.common.Common
import com.okayqa.composer.base.OkayBase
import com.okayqa.composer.function.OKClass

import java.util.concurrent.atomic.AtomicInteger

class BothCollect extends OkayBase {

static AtomicInteger u = new AtomicInteger(0)

static int times

static int thread

public static void main(String[] args) {
ClientManage.init(5, 1, 0, "", 0)
def util = new ArgsUtil(args)
thread = util.getIntOrdefault(0, 200)
times = util.getIntOrdefault(1, 100)
def funs = []
thread.times {
funs << new Fun()
}
new Concurrent(funs, "收藏和取消收藏").start()
allOver()
}

static int getTimes() {
return times
}

static class Fun extends ThreadLimitTimesCount {

OkayBase okayBase = getBase(u.getAndIncrement())

OKClass driver = new OKClass(okayBase)

public Fun() {
super(null, getTimes(), null)
}


@Override
protected void doing() throws Exception {
def collect = driver.collect()
def last = okayBase.getLast()
def value1 = okayBase.getLastRequestId() + CONNECTOR
this.threadmark += value
if (collect.getJSONObject("meta").getIntValue("ecode") != 0) fail(value + "请求出错!")
def collect1 = driver.unCollect()
def value1 = okayBase.getLastRequestId()
this.threadmark += value1
if (collect1.getJSONObject("meta").getIntValue("ecode") != 0) fail(value1 + "请求出错!")
}
}

}

记录方案实现

首先对​​Base​​​类进行改造,增加​​ private HttpRequestBase last;​​​属性,然后在​​ public JSONObject getResponse(HttpRequestBase httpRequestBase)​​方法中增加复制操作,如下:

@Override
public JSONObject getResponse(HttpRequestBase httpRequestBase) {
setHeaders(httpRequestBase);
recordRequest(httpRequestBase);
JSONObject response = FanLibrary.getHttpResponse(httpRequestBase);
handleResponseHeader(response);
return response;
}

然后增加​​last​​相关操作:

@Override
public void recordRequest(HttpRequestBase base) {
this.last = base;
}

/**
* 获取最后一个请求
*
* @return
*/
@Override
public HttpRequestBase getRequest() {
return last == null ? FanLibrary.getLastRequest() : last;
}

/**
* 获取最后一个请求的requestid
*
* @return
*/
public String getLastRequestId() {
HttpRequestBase httpRequestBase getLast();
return httpRequestBase.getFirstHeader(Common.REQUEST_ID.getName()).getValue();
}

这里用了一个非空判断,主要是为了防止空指针异常,有些​​Base​​​对象初始化并不是通过接口请求实现的。在进行批量​​Base​​对象创建和初始化的时候用的是单线程,代码如下:

def funs = []
thread.times {
funs << new Fun()
}

所以不存在线程安全的问题,故而采取了这种方案。

测试框架相关使用情况可以参考之前的视频讲解:

接口测试视频

  • FunTester测试框架视频讲解(序)
  • 获取HTTP请求对象--测试框架视频讲解
  • 发送请求和解析响应—测试框架视频解读
  • json对象基本操作--视频讲解
  • GET请求实践--测试框架视频讲解
  • POST请求实践--视频演示
  • 如何处理header和cookie--视频演示
  • FunRequest类功能--视频演示
  • 接口测试业务验证--视频演示
  • 自动化测试项目基础--视频讲解
  • JSONArray基本操作--视频演示
  • 自动化项目基类实践--视频演示
  • 模块类和自动化用例实践--视频演示
  • 性能框架多线程基类和执行类--视频讲解
  • 定时和定量压测模式实现--视频讲解
  • 基于HTTP请求的多线程实现类--视频讲解

Git仓库

  • ​Gitee​​地址​​gitee.com/fanapi/test…
  • ​GitHub​​地址​​/JunManYuanL…

FunTester,非著名测试开发,文章记录学习和感悟,欢迎关注,交流成长。