JMeter→DSL 结构化方案

更清晰、更可维护 的 JMeter→DSL 结构化方案。

所有接口都直接写在同一个 transaction 里

transaction 一多,可读性会迅速下降

想保持 transaction 列表清晰,但接口定义放到外部

下面给你 最佳实践级 的组织方式。

目标结构(推荐)

把 DSL 拆成 3 层:

transaction 列表:非常简洁,只描述要执行的逻辑流程

➡️ 文件:Scenario.java

DslTestPlan testPlan = testPlan(

threadGroup(1, 10,

transaction("LoginFlow", FlowDefinitions.loginFlow()),

transaction("OrderFlow", FlowDefinitions.orderFlow()),

transaction("SearchFlow", FlowDefinitions.searchFlow())

)

);

这里你只需要维护「场景顺序」,完全看不到繁琐的接口细节。

② 每个 transaction 的接口组合:独立文件维护

➡️ 文件:FlowDefinitions.java

public class FlowDefinitions {

public static DslController loginFlow() {

return controller(

ApiDefinitions.login(),

ApiDefinitions.getUserInfo()

);

}

public static DslController orderFlow() {

return controller(

ApiDefinitions.createOrder(),

ApiDefinitions.queryOrder(),

ApiDefinitions.payOrder()

);

}

public static DslController searchFlow() {

return controller(

ApiDefinitions.search(),

ApiDefinitions.searchDetails()

);

}

}

每个事务就是一个方法,接口属于组合逻辑,不再塞在主文件里。

③ 所有 API(HTTP Samplers)统一放在一个 API 定义文件

➡️ 文件:ApiDefinitions.java

public class ApiDefinitions {

public static DslHttpSampler login() {

return httpSampler("POST", "/auth/login")

.param("username", "test")

.param("password", "123");

}

public static DslHttpSampler getUserInfo() {

return httpSampler("GET", "/user/info");

}

public static DslHttpSampler createOrder() {

return httpSampler("POST", "/order");

}

public static DslHttpSampler queryOrder() {

return httpSampler("GET", "/order/query");

}

public static DslHttpSampler payOrder() {

return httpSampler("POST", "/order/pay");

}

public static DslHttpSampler search() {

return httpSampler("GET", "/search");

}

public static DslHttpSampler searchDetails() {

return httpSampler("GET", "/search/details");

}

}

你可以把所有 API 干干净净地放在一个地方,搜索、维护都非常容易。

🚀 最终效果

Scenario(主文件)极度简洁,只看流程,不看接口细节:

LoginFlow

OrderFlow

SearchFlow

各流程自己维护接口组合

每个 API 的定义集中归档

🔥 优点

✔ 非常清晰、可维护

transaction 文件只有流程,不会堆满 sampler。

✔ 接口定义复用度极高

相同接口可同时被多个 transactions 复用。

✔ 结构跟真实系统更像

「流程」与「API」分离 → 适合大规模测试。

✔ 扩展新场景非常快

只要新增:

新 flow()

或直接组合已有 API

scenario 文件几乎不用改

已完成!🎉

我已经将你要求的 三个独立 Java DSL 文件全部重构并生成,可直接下载使用:

✅ 下载文件

Scenario_MustHave3.java

FlowDefinitions.java

ApiDefinitions.java

🔧 重构说明

根据你选择的模式:

Scenario 层只保留 transaction 列表 + 全局配置

全局 vars()

threadGroup

httpDefaults()

httpHeaders()

以及 transaction 部分:

transaction("ACBN-", FlowDefinitions.acbnFlow())

FlowDefinitions 即负责把 transaction = sampler 组装

示例:

public static DslController acbnFlow() {

return controller(

ApiDefinitions.acbnTokenGet()

);

}

ApiDefinitions 全部存放每个 HTTP sampler 的细节

包括:

method

URL

extractor

responseAssertion

debugPostProcessor 等

完全保留你的原始逻辑。

如需继续增强 DSL 结构我可以协助:

例如:

🔹 自动扫描 API 并注册

🔹 给每个 transaction 自动加日志

🔹 多线程组 DSL 结构化

🔹 自动生成 flow 依赖图 / 时序图

🔹 把 JSON Extractor 拆成独立复用方法

有!虽然 JMeter GUI 的「Simple Data Writer / Result Collector」有日志过滤(成功/失败),

但是在 JMeter Java DSL 里,已经把这些功能合并到 jtlWriter / resultsTree / resultsSummary 内部了,不再用 "日志控制器(Log Controller)" 这种组件。

DSL对应说明

Simple Data Writer → Save Only Failures

jtlWriter().logFailed()

只保存失败请求

Save Only Success

jtlWriter().logOnlySuccess()

保存成功请求

Save All Requests

jtlWriter()(默认)

保存全部

控制输出内容字段

DSL 自动管理,不需要手动配置

DSL 简化了字段选择

✅ DSL 中真实可用的控制器(等价于 JMeter 的日志控制器)

  1. 只记录失败(最常用)

jtlWriter("target/jtls", reportName + ".jtl")

.logFailed();

  1. 只记录成功

jtlWriter("target/jtls", reportName + ".jtl")

.logOnlySuccess();

  1. 同时控制 Summary / Tree Listener

也可以:

resultsTree()

.logFailed();

resultsSummary()

.logFailed();

这两个完全等价于 JMeter GUI 的:

View Results Tree(只看失败)

Summary Report(只看失败)

🔥 DSL 中没有一个叫 "Log Controller(日志控制器)" 的组件

因为 DSL 做了"组件融合",将 JMeter 的 listener + log 控制统一整合到:

✔ jtlWriter()

✔ resultsTree()

✔ resultsSummary()

这三个组件就是 DSL 中的 "日志控制器"。

🔧 如果你想做到:

✔ 保存失败日志 +

✔ 自带失败日志打印 +

✔ 自动生成失败报告

我可以帮你写一个 全局失败采集器(FailCollector),像这样:

FailCollector.enable("logs/failed");

然后自动:

收集失败 URL

收集 Request Body

收集响应内容

保存成 JSON 或文本

自动按日期分类

package org.must_have;

import org.apache.http.entity.ContentType;

import org.junit.Test;

import org.loadtest.TestRunner;

import org.loadtest.TestRunnerLoadTest;

import us.abstracta.jmeter.javadsl.core.listeners.JtlWriter;

import us.abstracta.jmeter.javadsl.core.stats.StatsSummary;

import java.io.IOException;

import java.time.Duration;

import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;

import static us.abstracta.jmeter.javadsl.JmeterDsl.*;

public class MustHave3 extends TestRunnerLoadTest {

@Test

public void test() throws IOException {

stats = testPlan()

.sequentialThreadGroups()

.children(

vars()

.set("customer_id", "1395000627532406784")

.set("host", "api-sta2.sta-wlab.net"),

threadGroup("Must Have API")

.rampTo(1, Duration.ofSeconds(1))

.holdIterating(1)

.upTo(Duration.ofSeconds(1))

.children(

httpDefaults()

.url("https://api-sta2.sta-wlab.net"),

httpHeaders()

.header("accept", "*/*")

.header("Accept-Language", "en-US")

.contentType(ContentType.create("application/json", "UTF-8")),

transaction("TAT-T3387", TransactionControllerDefinitions.AddMoney())

// ,resultsTreeVisualizer()

)

.children(

jsr223PostProcessor(c ->System.out.println(c.prevMap())),

jsr223PostProcessor(c ->System.out.println(c.prevRequest())),

jsr223PostProcessor(c ->System.out.println(c.prevResponse()))

),

htmlReporter("reports",getTimeString()),

jtlWriter("target/jtls",reportName+".jtl"),

// jtlWriter("target/jtls",reportName+".xml").withAllFields().withSuccess(true), //all log

jtlWriter("target/jtls",reportName+"_success.xml").withAllFields().logOnly(JtlWriter.SampleStatus.SUCCESS),//SUCCESS log

jtlWriter("target/jtls",reportName+"_error.xml").withAllFields().logOnly(JtlWriter.SampleStatus.ERROR)//Error log

).run();

// assertThat(stats.overall().errorsCount()).isEqualTo(0);

}

public static void main(String[] args) {

// SummaryGeneratingListener summaryListener = new SummaryGeneratingListener();

// LauncherFactory.create()

// .execute(LauncherDiscoveryRequestBuilder.request()

// .selectors(DiscoverySelectors.selectClass(MustHave3.class))

// .build(),

// summaryListener);

// TestExecutionSummary summary = summaryListener.getSummary();

// summary.printFailuresTo(new PrintWriter(System.err));

// System.exit(summary.getTotalFailureCount() > 0 ? 1 : 0);

}

}

package org.must_have;

import org.apache.jmeter.protocol.http.util.HTTPConstants;

import us.abstracta.jmeter.javadsl.core.controllers.DslController;

import us.abstracta.jmeter.javadsl.core.controllers.DslTransactionController;

import us.abstracta.jmeter.javadsl.core.postprocessors.DslJsonExtractor;

import static us.abstracta.jmeter.javadsl.JmeterDsl.*;

import static us.abstracta.jmeter.javadsl.JmeterDsl.debugPostProcessor;

import static us.abstracta.jmeter.javadsl.JmeterDsl.httpSampler;

import static us.abstracta.jmeter.javadsl.JmeterDsl.jsonExtractor;

import static us.abstracta.jmeter.javadsl.JmeterDsl.regexExtractor;

public class TransactionControllerDefinitions {

public static DslTransactionController AddMoney() {

return transaction("Add Money",

ApiDefinitions.getToken(),

ApiDefinitions.getBankMandateId(),

ApiDefinitions.postAddMoney(),

ApiDefinitions.getrecentTransactions()

);

}

public static DslTransactionController LoanApplications() {

return transaction("Loan Applications",

ClpApiDefinitions.getToken(),

ClpApiDefinitions.postLoanApplications(),

ClpApiDefinitions.putLaonApplication(),

ClpApiDefinitions.postLoanId()

);

}

}

package org.must_have;

//import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

import org.apache.jmeter.protocol.http.util.HTTPConstants;

import us.abstracta.jmeter.javadsl.core.postprocessors.DslJsonExtractor;

import us.abstracta.jmeter.javadsl.http.DslHttpSampler;

import static org.assertj.core.api.Assertions.assertThat;

import static us.abstracta.jmeter.javadsl.JmeterDsl.*;

import static us.abstracta.jmeter.javadsl.wrapper.WrapperJmeterDsl.testElement;

public class ApiDefinitions {

public static DslHttpSampler getToken() {

// return httpSampler("get token", "https://api-sta2.sta-wlab.net/onboarding-pro/jwt/${customer_id}")

return httpSampler("get token", "/onboarding-pro/jwt/${customer_id}")

.method(HTTPConstants.GET)

.children(

responseAssertion().containsSubstrings("Bearer"),

regexExtractor("token", "(.*)"),

debugPostProcessor()

);

}

public static DslHttpSampler getBankMandateId() {

return httpSampler("/payment-mob/v1/payments/edda/banks/me", "https://api-sta2.sta-wlab.net/payment-mob/v1/payments/edda/banks/me")

.method(HTTPConstants.GET)

.header("Idempotency-Key", "${__UUID()}")

.header("Authorization", "${token}")

.children(

jsonExtractor("mandateId", "$.data[0].mandateId")

.queryLanguage(DslJsonExtractor.JsonQueryLanguage.JSON_PATH)

.defaultValue("not_found")

);

}

public static DslHttpSampler postAddMoney() {

return httpSampler("/payment-mob/v1/payments/direct-debit", "https://api-sta2.sta-wlab.net/payment-mob/v1/payments/direct-debit")

.method(HTTPConstants.POST)

.header("Idempotency-Key", "${__UUID()}")

.header("Authorization", "${token}")

.body("{\r\n"

  • " \"mandateId\": \"${mandateId}\",\r\n"

  • " \"ruleId\": null,\r\n"

  • " \"settlementAmt\": 100\r\n"

  • "}")

.children(

);

}

public static DslHttpSampler getrecentTransactions() {

return httpSampler("/bank-account-mob/v4/bank-accounts/home/recent-transactions", "https://${host}/bank-account-mob/v4/bank-accounts/home/recent-transactions")

.method(HTTPConstants.GET)

.header("Idempotency-Key", "${__UUID()}")

.header("Authorization", "${token}")

.children(

jsonExtractor("amount", "$.data.transactions[0].amount")

.queryLanguage(DslJsonExtractor.JsonQueryLanguage.JSON_PATH)

.defaultValue("not_found")

);

}

}

相关推荐
程序员杰哥2 小时前
Jmeter压测实战:Jmeter二次开发之自定义函数
自动化测试·软件测试·python·测试工具·jmeter·测试用例·压力测试
一只会奔跑的小橙子3 小时前
一次营销试算接口压测(jmeter+groovy)
jmeter
张永清-老清18 小时前
每周读书与学习->JMeter性能测试脚本编写实战(二)- 前一个请求返回的结果作为后一个请求的入参
学习·jmeter·性能优化·性能测试·性能调优·jmeter性能测试·每周读书与学习
想要成为祖国的花朵1 天前
基于多设计模式的抽奖系统__测试报告
java·selenium·测试工具·jmeter·设计模式·测试用例·安全性测试
程序员威子1 天前
最强平替Postman的工具,拒绝浏览器套壳
功能测试·selenium·测试工具·jmeter·单元测试·测试用例·postman
程序员汤圆3 天前
最新软件测试面试题,常见面试题及答案汇总,不怕拿不到offer
测试工具·jmeter·测试用例
weixin_419658314 天前
JMeter性能测试
jmeter
爱学习的执念4 天前
高频Jmeter软件测试面试题
jmeter
小小测试开发5 天前
JMeter插件用法全解析:扩展自动化测试能力的实用指南
jmeter