Gatling基于配置生成断言异常:多场景断言复用同一请求名排查
问题背景
正在编写Gatling仿真脚本,通过配置文件读取一组带属性的API信息,流程如下:
- 读取配置获取n个活跃场景,通过CommonRequest类创建请求
- 将请求通过CommonScenario构建为场景
- 利用CommonScenario的属性生成注入配置
前三个步骤均正常,但基于CommonScenario属性构建断言时不符合预期。
代码片段
// get active scenarios from the config val activeApiScenarios: List[String] = Utils.getStringListProperty("my.active_scenarios") // build all active scenarios from config var activeScenarios: Set[CommonScenario] = Set[CommonScenario]() activeApiScenarios.foreach { scenario => activeScenarios += CommonScenarioBuilder() .withRequestName(Utils.getProperty("my." + scenario + ".request_name")) .withRegion(Utils.getProperty("my." + scenario + ".region")) .withConstQps(Utils.getDoubleProperty("my." + scenario + ".const_qps")) .withStartQps(Utils.getDoubleListProperty("my." + scenario + ".toth_qps").head) .withPeakQps(Utils.getDoubleListProperty("my." + scenario + ".toth_qps")(1)) .withEndQps(Utils.getDoubleListProperty("my." + scenario + ".toth_qps")(2)) .withFeeder(Utils.getProperty("my." + scenario + ".feeder")) .withAssertionP99(Utils.getDoubleProperty("my." + scenario + ".p99_lte_assertion")) .build } // build population builder set by adding inject profile values to scenarios var injectScenarios: Set[PopulationBuilder] = Set[PopulationBuilder]() var assertions : Set[Assertion] = Set[Assertion]() activeScenarios.foreach { scenario => // create injection profiles from CommonScenarios injectScenarios += scenario.getCommonScenarioBuilder .inject(nothingFor(5 seconds), rampUsersPerSec(scenario.startQps).to(scenario.rampUpQps).during(rampOne seconds), rampUsersPerSec(scenario.rampUpQps).to(scenario.peakQps).during(rampTwo seconds), rampUsersPerSec(scenario.peakQps).to(scenario.rampDownQps) during (rampTwo seconds), rampUsersPerSec(scenario.rampDownQps).to(scenario.endQps).during(rampOne seconds)).protocols(httpProtocol) // create scenario assertions this does not work for some reason assertions += Assertion(Details(List(scenario.requestName)), TimeTarget(ResponseTime, Percentiles(4)), Lte(scenario.assertionP99)) } setUp(injectScenarios.toList) .assertions(assertions)
注:scenario.requestName直接来自构建好的场景,场景包含
.feed(feederBuilder).exec(commonRequest)逻辑。
预期与实际现象
- 预期:每个场景对应的断言能被正确构建并传入
setUp() - 实际现象:打印断言时,所有断言复用同一个Request Name,仅Lte值不同(配置了12个API,各有不同名称和阈值),仿真后所有断言结果均为false。
问题分析与解决方案
核心问题:CommonScenario类的equals/hashCode实现缺陷
你使用Set[CommonScenario]存储构建好的场景,但如果CommonScenario类没有基于关键属性正确实现equals和hashCode方法,Set会误判多个不同实例为同一对象,最终Set中只会保留最后一个场景实例。遍历Set时所有迭代都复用该实例,导致断言的requestName全部重复。
验证方式
构建完activeScenarios后打印其大小,若实际大小远小于配置的12个,即可确认是此问题。
修复方案
改用Case Class:将
CommonScenario定义为Scala的case class,case class会自动基于所有成员属性生成正确的equals和hashCode方法,避免Set去重错误:case class CommonScenario( requestName: String, region: String, constQps: Double, startQps: Double, peakQps: Double, endQps: Double, feeder: String, assertionP99: Double // 其他必要属性 )对应的Builder类也需要基于此case class构建实例。
替换集合类型:将
Set[CommonScenario]改为List[CommonScenario],List不会自动去重,能完整保留所有构建的场景实例,规避equals/hashCode带来的问题。
额外验证点
修复实例存储问题后,需确认scenario.requestName与commonRequest中设置的请求名称完全一致(包括大小写、空格等),Gatling断言采用精确匹配,名称不匹配会导致断言结果异常。
方案可行性
该整体方案是可行的,解决场景实例存储问题后,每个场景对应独立断言的逻辑就能正常运行。
内容的提问来源于stack exchange,提问作者gclark82

