性能测试工具 使用 JunitPerf 进行性能测试介绍

simonpatrick · 2018年07月24日 · 最后由 gx 回复于 2018年08月12日 · 4850 次阅读
本帖已被设为精华帖!

使用 JunitPerf 进行性能测试

以下简单介绍一下如何使用 JunitPerf 进行性能测试,JunitPerf 是基于 JUnit4 的一个单元性能测试插件,对于会远程调用 API 测试比较合适,如果想要比较 nanosecond 延迟的则需要使用JMH.
使用 JunitPerf 测试一些 SDK 的性能,个人觉得还是不错,而且比 JMeter 使用起来会简单一些,不需要写 JMeter 插件.

JunitPerf 依赖声明

此例子假设使用 MAVEN 管理项目,所以在 POM 文件中添加:

<dependency>
 <groupId>junit</groupId>
 <artifactId>junit</artifactId>
 <version>4.12</version>
</dependency>
<dependency>
 <groupId>com.github.noconnor</groupId>
 <artifactId>junitperf</artifactId>
 <version>1.9.1</version>
</dependency>

构建压力测试类

假设你想衡量 DemoPerfService 类中的 getServiceId 方法:

public class DemoPerfService {

    public String getServiceId(String userId){

        return UUID.randomUUID().toString();
    }
}

那么你可以构建如下的测试类:

public class DemoServiceTest {
    @Rule
    public JUnitPerfRule perfTestRule = new JUnitPerfRule();
    DemoPerfService demoPerfService;
    @Before
    public void setupService(){
        this.demoPerfService = new DemoPerfService();
    }
    @Test
    @JUnitPerfTest(threads = 50,durationMs = 1200,warmUpMs = 100,maxExecutionsPerSecond = 110)
    public void getServiceId_withoutTestRequirement() {

        String result =demoPerfService.getServiceId("userid");
        System.out.println(result);
        Assert.assertNotNull(result);
    }

直接运行就可以进行压力测试,默认的测试报告可以在 build/reports 目录下获取:

以下是对于测试类的几点说明:

Item 定义说明 Default 值或说明
@Rule 申明为 JUnit 的 Rule 类
JUnitPerfRule JUnitPerf 测试规则类
@JunitPerfTest 声明为性能测试方法
threads 测试使用的线程数
durationMs 测试持续时间
warmUpMs 测试热身时间 热身时间的测试数据不会计算进最后的测试结果
maxExecutionsPerSecond 方法执行的上限 RateLimiter,控制 TPS 上限

对自己的测试设置期望值

使用@JUnitPerfTestRequirement 可以给性能测试设置期望值,这个 annotation 的属性有:

属性 定义
percentits 设置例如 90%/95%/50% 响应时间的期望
executionsPerSec 期望每秒执行测试 (TPS)
allowedErrorPercentage 允许错误比例
minLatency 期望最小延时,如果实际最小延时超过这个数,则失败
maxLatency 期望最大延时,如果实际最大延时超过这个,则失败
meanLatency 期望中位数延时

下面是使用了 JUnitPerfTestRequirement 的一个测试方法,需要和@JUnitPerfTest一起使用:

具体代码如下例:

@Test
  @JUnitPerfTest(threads = 50,durationMs = 1200,warmUpMs = 100,maxExecutionsPerSecond = 110)
  @JUnitPerfTestRequirement(percentiles = "90:7,95:7,98:7,99:8", executionsPerSec = 10_000, allowedErrorPercentage = 0.10f)
  public void getServiceId() {

      String result =demoPerfService.getServiceId("userid");
      System.out.println(result);
      Assert.assertNotNull(result);
  }

运行之后,如果发现没有满足 JUnitPerfTestRequirement 定义,则报错:

java.lang.AssertionError: Test throughput threshold not achieved
Expected: is <true>
     but: was <false>
Expected :is <true>

是不是很简单!

设置测试报告地址

JUnitPerf 有不同的测试报告,个人觉得 HTML 的测试报告比较实用,具体只需要:

@Rule
public JUnitPerfRule perfTestRule = new JUnitPerfRule(new HtmlReportGenerator("perf/report.html"));

完整的例子

public class DemoServiceTest {
    @Rule
//    public JUnitPerfRule perfTestRule = new JUnitPerfRule(new HtmlReportGenerator("perf/report.html"));
    public JUnitPerfRule perfTestRule = new JUnitPerfRule();
    DemoPerfService demoPerfService;
    @Before
    public void setupService(){
        this.demoPerfService = new DemoPerfService();
    }

    @Test
    @JUnitPerfTest(threads = 50,durationMs = 1200,warmUpMs = 100,maxExecutionsPerSecond = 110)
    @JUnitPerfTestRequirement(percentiles = "90:7,95:7,98:7,99:8", executionsPerSec = 10_000, allowedErrorPercentage = 0.10f)
    public void getServiceId() {

        String result =demoPerfService.getServiceId("userid");
        System.out.println(result);
        Assert.assertNotNull(result);
    }

    @Test
    @JUnitPerfTest(threads = 50,durationMs = 1200,warmUpMs = 100,maxExecutionsPerSecond = 110)
    public void getServiceId_withoutTestRequirement() {

        String result =demoPerfService.getServiceId("userid");
        System.out.println(result);
        Assert.assertNotNull(result);
    }
}

最后可以再设定的目录中查看测试报告,测试报告和默认的 HTML 测试报告是一致的.

一点问题

压力测试过程中,有时数据不能复用,举个例子来说,如果想测试完全没有访问 redis 缓存情况下,通过 userid 查询的 user 信息速度,那么压测的时候 userid 就不能复用,因为一旦访问了就会放入 redis 缓存而影响结果,这个可以通过使用其他的方法解决,比如曾今使用过 BlockingQueue 的方法进行过尝试,具体方法如下:

  1. 读取所有 userid 的文件
  2. 把 userid 放到一个 BlockingQueue 中
  3. 压测时获取 userid 通过 BlockingQueue 去获取

这样就解决了数据不能重复的方法,具体方法可以参考如下代码:

    static BlockingQueue<String> distinctIdQueue ;
    @Rule
    public JUnitPerfRule perfTestRule =
            new JUnitPerfRule(new HtmlReportGenerator("data/report_test.html"));

    @BeforeClass
    public static void setupQueue() throws IOException {

        distinctIdQueue = new LinkedBlockingQueue<>();
        Files.readAllLines(
                Paths.get("data/userid.txt")
        ).parallelStream().forEach(
                item-> {
                    try {
                        distinctIdQueue.put(item);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
        );

    }

     @Test
    @JUnitPerfTest(threads = 50,durationMs = 1200,warmUpMs = 100,maxExecutionsPerSecond = 110)
    @JUnitPerfTestRequirement(percentiles = "90:7,95:7,98:7,99:8", executionsPerSec = 10_000, allowedErrorPercentage = 0.10f)
    public void getServiceId() {
        String uesrId = distinctIdQueue.take();
        String result =demoPerfService.getServiceId(userId);
        System.out.println(result);
        Assert.assertNotNull(result);
    }


}
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 3 条回复 时间 点赞
思寒_seveniruby 将本帖设为了精华贴 07月28日 10:09

赞一个 基于 junit 的性能测试,不需要自己开线程,但是一般压测在 linux 下,还需要打包哟。

蓝蓝 回复

直接在本机运行的,没有打包。maven 的项目的话,直接 mvn test 就可以了,或者在打包的时候不忽律 test,就会在打包时运行.

对多线程有用处,用了 java8 的技术

simple 专栏文章:[精华帖] 社区历年精华帖分类归总 中提及了此贴 12月13日 14:44
simple [精彩盘点] TesterHome 社区 2018 年 度精华帖 中提及了此贴 01月07日 12:08
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册