65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 JUnit 和 Maven 轻松进行性能测试

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2018年7月4日

CPOL

8分钟阅读

viewsIcon

29476

本文介绍了如何使用 JUnit 和 Maven 进行性能测试,也称为负载/压力测试。

本文已分为以下几个高级部分

引言

本文强调了性能测试的必要性,以及如何使用简单的 JUnit 和 Maven 基于开源库轻松、准确地进行性能测试。我们在应用程序的负载和压力测试中通常面临哪些问题,以及如何克服这些问题,并将performance-testing(性能测试)作为 CI 构建管道的一部分。

问题

有时,我们倾向于认为性能测试不属于开发过程的一部分。这可能是因为在正常的开发冲刺中没有为此创建故事。这意味着产品或服务 API 的重要方面没有得到关注。但这并不是重点,重点是我们为什么要认为它不应该成为常规开发周期的一部分?或者...

为什么我们要把它放在项目周期的最后?

此外,为了支持上述观点,进行性能测试不像进行单元测试 功能/组件测试端到端集成测试消费者-契约测试那样有直接的方法。然后,要求开发人员或性能测试人员(有时是专业团队)从市场选择一个独立工具,并生成一些漂亮的性能测试报告,与业务或技术团队共享这些报告。

这意味着它是孤立进行的,有时是在开发冲刺完成之后或临近完成时进行的,接近生产发布日期。因此,错过了这项测试的重要性,没有为改进或修复产品中的潜在问题留出空间。回答了第一个问题“为什么”。

理想情况下,将其作为常规开发周期的一部分

性能测试应成为 CI 构建的一部分。

并且要使其成为 CI 构建的一部分,它必须足够容易,以便开发人员或性能测试人员可以编写/更改/共享测试。

关键在于,我们如何获得开发人员容易理解的有用统计数据,或者如何修复负载/压力/容量测试中出现的问题,或者

我们如何从 CI 构建中获得持续反馈?

在传统方法中,花费大量时间来理解工具和使工具工作,因为有些工具不适合 IDE,换句话说,甚至不是基于 Maven/JUnit 的。

一些挑战,例如将性能测试工具指向技术栈中的任何地方,都不是一个容易或直接的任务,例如,指向 REST 端点、SOAP 端点、DB 服务器或 KAFKA 主题或 MQ 端点(如 ActiveMQ/WMQ)、SSL 连接、通过企业代理的连接等。

这使得隔离问题变得有些困难,即您的应用程序API 性能非常好,但只有下游系统存在问题。让我们解释一下这意味着什么。

例如

您刚刚使用独立工具测试了 GET API 的性能,指向 URL,例如 /api/v1/id-checks/131001,并发现响应延迟比单次调用同一个 API 时要长。然后,您(作为开发人员)倾向于将其归咎于 DB 或 MQ 主题,例如,给出的理由是 Oracle DB 服务器在处理并行负载时速度很慢。但是,您如何提供证据来支持您的论点,即您的 API 并不慢?

现在,您希望有一个机制或工具来隔离这个问题,方法是将性能测试工具直接指向 DB 或 KAFKA 主题等,因为(作为开发人员)您知道——哪些 SQL 查询被发送到 DB 以获取结果您知道可以从中直接获取数据的主题名称绕过应用程序 API 处理层,这可以更有意义地证明您的观点并提供证据。

要实现这些步骤,实际上应该非常容易,因为您已经拥有现有的 JUnit testsintegration-tests 等,它们每天都在做同样的事情(实际上,每个 CI 构建都在这样做)。但有时使用独立的或市场上的性能工具很难做到,因为

您还没有找到一种方法来重用现有测试来生成负载/压力。

并且/或者它不够灵活,无法将这些现有测试馈送到您选择的工具中。所以您失去了兴趣,或者在冲刺中没有足够的时间对其进行探测,然后,您跳过了这项测试的方面,将责任推卸给下游系统或工具。

潜在解决方案

理想情况下,您需要一个自定义的JUnit 负载运行器,它可以轻松地让您重用现有的 JUnit tests端到端集成测试功能测试组件测试,因为它们大多数在后台使用 JUnit 或 TestNG)来生成负载或压力(在此处阅读负载与压力的区别),也称为水平负载与垂直负载)。

理想情况下,负载运行器应该如下所示,可以解决问题

@LoadWith("your_load_config.properties")
@TestMapping(testClass = YourExistingEndPointTest.class, testMethod = "aTest")
@RunWith(YourLoadRunner.class)
public class LoadTest {

}

其中 your_load_config.properties 应包含以下属性

number.of.parallel.users=50
users.to.be.ramped.up.in.seconds=50
repeat.this.loop.times=2

其中您希望在50 秒内50 个用户加载起来(每个用户以1 秒的间隔触发测试),并且此操作应运行两次(loop=2),即总共100 个并行用户将触发请求,每个请求大约以1 秒的间隔

@TestMapping 意味着

@TestMapping(testClass = YourExistingEndPointTest.class, testMethod = "aTest")

您上面的 YourExistingEndPointTestaTest 方法应具有断言,以将实际接收到的结果预期结果进行匹配。

负载运行完成后,您可能需要一个如下所示的精确统计数据

触发的总测试次数 100
通过的总测试次数 90
失败的总测试次数 10
请求之间的平均延迟(秒) 1
平均响应时间延迟(秒) 5

并且理想情况下,可以按需绘制更多统计数据,前提是您的 YourLoadRunner 可以生成CSV/电子表格,其中包含以下类型的数据(或更多)

测试类名 TestMethod 唯一测试 ID 请求时间戳 响应延迟 响应时间戳 结果
您的现有测试 aTest test-id-001 2018-06-09T21:31:38.695 165 2018-06-09T21:31:38.860 通过
您的现有测试 aTest test-id-002 2018-06-09T21:31:39.695 169 2018-06-09T21:31:39.864 失败

当然,这些都是基本内容,您应该可以使用任何选定的工具来完成所有这些测试。

但是,如果您想为每个用户并发地触发不同类型的请求怎么办?

那么关于

  • 一个用户想要触发POST,然后是 GET
  • 另一个用户继续触发POST > 然后 GET > 然后 PUT > 然后 GET 来验证所有CRUD 操作是否正常?
  • 另一个用户每次触发请求时动态更改有效负载
  • 等等…每个场景都断言每个测试结果?
  • 如果您想逐渐增加或减少被测应用程序的负载怎么办?
  • 一个用户产生压力,另一个用户产生负载,两者同时进行?

现在您肯定需要一个机制来重用现有测试,因为您可能已经在常规端到端测试中测试了这些场景(顺序执行,但不是并行执行)。

所以您可能需要一个如下所示的 JUnit 运行器,它可以减轻重复做同样事情的痛苦。

使用开源库(JUnit 和 Maven 库)

最近,在进行性能测试时,我的团队偶然发现了这个名为 ZeroCode开源 Maven 库(请参阅 GitHub 上的 README),它提供了方便的 JUnit负载运行器,用于负载测试,使性能测试成为一项轻松的工作。我们富有创意地提出了许多场景,并成功地对我们的应用程序进行了负载测试。当然,我们同时将这些测试添加到了准备进行 CI 构建的负载回归包中。这有多棒!

这意味着我们能够简单地映射现有的 JUnit 测试到负载运行器。

基本上,我们将这两个库,即JUnitZerocode 结合起来生成负载/压力

  1. JUnit(非常流行的开源项目,在 Java 社区中常用)
  2. Zerocode(因其易于断言的 BDD/TDD 自动化而日益流行)

查看基本示例

用法示例?

  1. 如需示例性能测试,请浏览 Maven performance-test 项目
  2. 下载(zip)并运行性能测试,解压缩并像普通 Maven 项目一样导入
  3. 浏览 helloworld 项目,了解 TDD/BDD 自动化的简单方法

关注点(报告、日志等)

当测试运行统计数据生成为 CSV 文件时,您可以使用这些数据集绘制任何图表。

这个库特别生成两种报告(请参阅此处示例负载测试报告

  1. CSV 报告(位于 target/zerocode-junit-granular-report.csv
  2. 交互式模糊搜索和过滤 HTML 报告(位于 target/zerocode-junit-interactive-fuzzy-search.html

最重要的是,有时测试会失败,我们需要知道特定测试用例实例失败的原因。

如何跟踪失败的测试,包括请求/响应/请求时间/响应时间/失败原因?

您可以通过多种参数来跟踪失败的测试,但最简单的方法是使用其唯一步骤 ID。如何操作?

在 CSV 报告(以及 HTML 报告)中,您会找到一个名为 correlationId 的列,其中包含每次运行时测试步骤的唯一 ID。只需获取此 ID,然后在 target/zerocode_rest_bdd_logs.log 文件中搜索,您将获得与 TEST-STEP-CORRELATION-ID 匹配的完整详细信息,如下所示。

例如

2018-06-13 21:55:39,865 [main] INFO 
org.jsmart.zerocode.core.runner.ZeroCodeMultiStepsScenarioRunnerImpl - 
--------- TEST-STEP-CORRELATION-ID: a0ce510c-1cfb-4fc5-81dd-17901c7e2f6d ---------
*requestTimeStamp:2018-06-13T21:55:39.071
step:get_user_details
url:https://api.github.com:443/users/octocat
method:GET
request:
{ } 
--------- TEST-STEP-CORRELATION-ID: a0ce510c-1cfb-4fc5-81dd-17901c7e2f6d ---------
Response:
{
  "status" : 200,
  "headers" : {
    "Date" : [ [ "Wed, 13 Jun 2018 20:55:39 GMT" ] ],
    "Server" : [ [ "GitHub.com" ] ],
    "Transfer-Encoding" : [ [ "chunked" ] ],
    "X-RateLimit-Limit" : [ [ "60" ] ],
    "Vary" : [ [ "Accept" ] ]
    "Status" : [ [ "200 OK" ] ]
  },
  "body" : {
    "login" : "octocat",
    "id" : 583231,
    "updated_at" : "2018-05-23T04:11:38Z"
  }
}
*responseTimeStamp:2018-06-13T21:55:39.849 
*Response delay:778.0 milli-secs 
---------> Assertion: <----------
{
  "status" : 200,
  "body" : {
    "login" : "octocat-CRAZY",
    "id" : 583231,
    "type" : "User"
  }
} 
-done-

java.lang.RuntimeException: Assertion failed for :- 

[GIVEN-the GitHub REST end point, WHEN-I invoke GET, THEN-I will receive the 200 status with body] 
|
|
+---Step --> [get_user_details] 

Failures:
--------- 
Assertion path '$.body.login' with actual value 'octocat' 
did not match the expected value 'octocat-CRAZY'

并且可以绘制如下样本的吞吐量结果

当然,您可以使用Microsoft Excel 或任何其他方便的工具绘制折线图饼图、3D 图表等(还有更多功能取决于您的业务需求)。

历史

以前,该库只提供一个负载运行器,但可以保留结构不变地操纵有效负载。但从最近的版本(v1.2.2 及更高版本)开始,可以加载多个并行用户,每个用户都可以动态更改有效负载。这有助于模拟生产环境的场景。

© . All rights reserved.