You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Karate Feature中初始化的Java类在各场景复用不重复刷新?

问题:Karate中Java类初始化重复调用服务导致性能损耗

在Karate Feature文件的Background中,我通过调用RequestPublicKey和SessionRegister两个服务初始化了Java加密类SecureChannel,但Background会在每个场景重复执行,导致这两个服务被反复调用,给Gatling性能测试带来不必要的性能损耗。

如果不调用这两个服务初始化Java类,类中的方法会返回null。我试过在不同Feature中用call、callsingle、callonce调用,方法还是返回null;也尝试过用JS实现,都没达到预期效果。

我的Background代码如下:

Background:
* def conf = call read('../karate-config.js')
* url conf.whiteboxUrlUat
* def requestFile = read('data.json')

#Database Configurations
* def dbJsFile = call read('../database-configurations.js')
* def dbConfigurations = dbJsFile.sqlServerConfigurations
* def DbUtils = Java.type('testrails.DbUtils')
* def db = new DbUtils(dbConfigurations)

Given path 'api/SecureChannel/RequestPublicKey'
* def requestBody = requestFile.requestPublicKey
Given request requestBody
When method POST
* java.lang.Thread.sleep(1000)
Then print response
* def secureChannelPublicKey = response.PublicKey
* def scrChnl = Java.type('testrails.SecureChannel')
* def scr = new scrChnl(secureChannelPublicKey,"PB10203S60738","")
* match response.ErrorCode == 0

Given path 'api/SecureChannel/SessionRegister'
* def requestBody = requestFile.sessionRegister
Given request requestBody
* def sessionRegisterEncData = scr.generateSessionRegisterEncData()
* requestBody.EncData = sessionRegisterEncData
When method POST
Then print response
* scr.decryptData(response.EncData)
* match response.ErrorCode == 0

场景示例(通过scr.调用加密方法):

Scenario: Register
* url conf.mposEntMainUrl
Given path 'api/MainAppExternal/Register'
* def requestBody = requestFile.register
* def requestBodyConvertString = scr.mapToString(requestBody)
* def secureContentOlustur = scr.initSecureRequest(requestBodyConvertString,"PB99999999999")
Then print secureContentOlustur.secureContent
* def secureBody = requestFile.register2
* secureBody.SecureContent = secureContentOlustur.secureContent
Given request secureBody
When method POST
Then print response
Then print scr.generateRandomAesKey()
* match response.Code == 0

解决方案

方法1:将初始化逻辑抽离为独立Feature,用callonce全局调用

把RequestPublicKey、SessionRegister调用和Java类初始化的逻辑单独放到一个新的Feature文件中,然后在karate-config.js里用callonce调用这个Feature,确保初始化只执行一次。

  1. 创建SecureChannelInit.feature:
Feature: 初始化SecureChannel类

Scenario: 执行服务调用并初始化
* url conf.whiteboxUrlUat
* def requestFile = read('data.json')

Given path 'api/SecureChannel/RequestPublicKey'
* def requestBody = requestFile.requestPublicKey
Given request requestBody
When method POST
* java.lang.Thread.sleep(1000)
* def secureChannelPublicKey = response.PublicKey
* def scrChnl = Java.type('testrails.SecureChannel')
* def scr = new scrChnl(secureChannelPublicKey,"PB10203S60738","")
* match response.ErrorCode == 0

Given path 'api/SecureChannel/SessionRegister'
* def requestBody = requestFile.sessionRegister
Given request requestBody
* def sessionRegisterEncData = scr.generateSessionRegisterEncData()
* requestBody.EncData = sessionRegisterEncData
When method POST
* scr.decryptData(response.EncData)
* match response.ErrorCode == 0

# 将初始化好的scr对象返回,供全局使用
* return scr
  1. 修改karate-config.js,在全局配置中加入callonce:
function fn() {
  var config = {
    whiteboxUrlUat: 'your-upl-url',
    mposEntMainUrl: 'your-main-url'
    // 其他配置...
  };

  // 全局初始化SecureChannel,只执行一次
  config.scr = karate.callonce('classpath:path/to/SecureChannelInit.feature');

  return config;
}
  1. 在原Feature中直接使用全局的scr对象,删掉Background中的初始化逻辑:
Background:
* def conf = call read('../karate-config.js')
* def scr = conf.scr  // 直接使用全局初始化好的对象
* def requestFile = read('data.json')

# 保留其他必要配置,比如数据库部分
* def dbJsFile = call read('../database-configurations.js')
* def dbConfigurations = dbJsFile.sqlServerConfigurations
* def DbUtils = Java.type('testrails.DbUtils')
* def db = new DbUtils(dbConfigurations)

场景部分无需修改,依然可以用scr.调用方法。

方法2:修改Java类,实现单例模式+延迟初始化

如果可以修改SecureChannel的Java代码,直接把它改成单例模式,并且在第一次调用方法时自动执行RequestPublicKey和SessionRegister的调用逻辑,避免在Karate中重复初始化。

示例Java代码思路:

public class SecureChannel {
    private static SecureChannel instance;
    private String publicKey;
    // 其他成员变量...

    // 私有构造方法
    private SecureChannel(String publicKey, String param2, String param3) {
        this.publicKey = publicKey;
        // 初始化逻辑...
    }

    // 全局获取实例的方法,包含服务调用逻辑
    public static synchronized SecureChannel getInstance() {
        if (instance == null) {
            // 1. 调用RequestPublicKey服务获取公钥
            String publicKey = callRequestPublicKeyService();
            // 2. 创建实例
            instance = new SecureChannel(publicKey, "PB10203S60738", "");
            // 3. 调用SessionRegister服务完成初始化
            callSessionRegisterService(instance);
        }
        return instance;
    }

    // 封装调用RequestPublicKey的逻辑
    private static String callRequestPublicKeyService() {
        // 用Java HTTP客户端调用服务,返回PublicKey
        // 实现细节略
    }

    // 封装调用SessionRegister的逻辑
    private static void callSessionRegisterService(SecureChannel instance) {
        // 调用服务并处理加密/解密
        // 实现细节略
    }

    // 原有的业务方法
    public String generateSessionRegisterEncData() {
        // 实现...
    }

    // 其他方法...
}

然后在Karate中直接获取单例实例,无需每次调用服务:

Background:
* def conf = call read('../karate-config.js')
* url conf.whiteboxUrlUat
* def requestFile = read('data.json')

#Database Configurations
* def dbJsFile = call read('../database-configurations.js')
* def dbConfigurations = dbJsFile.sqlServerConfigurations
* def DbUtils = Java.type('testrails.DbUtils')
* def db = new DbUtils(dbConfigurations)

# 直接获取单例实例,初始化只执行一次
* def scrChnl = Java.type('testrails.SecureChannel')
* def scr = scrChnl.getInstance()

方法3:利用Karate的karate.scope存储全局对象

如果不想修改Java类,也可以在第一次初始化后把scr对象存入karate.scope,后续场景直接从全局作用域读取,避免重复初始化。

修改原Background逻辑,加入判断:

Background:
* def conf = call read('../karate-config.js')
* url conf.whiteboxUrlUat
* def requestFile = read('data.json')

#Database Configurations
* def dbJsFile = call read('../database-configurations.js')
* def dbConfigurations = dbJsFile.sqlServerConfigurations
* def DbUtils = Java.type('testrails.DbUtils')
* def db = new DbUtils(dbConfigurations)

# 检查全局作用域是否已有初始化好的scr对象
* def scr = karate.scope.scr
* if (!scr) {
    Given path 'api/SecureChannel/RequestPublicKey'
    * def requestBody = requestFile.requestPublicKey
    Given request requestBody
    When method POST
    * java.lang.Thread.sleep(1000)
    * def secureChannelPublicKey = response.PublicKey
    * def scrChnl = Java.type('testrails.SecureChannel')
    * scr = new scrChnl(secureChannelPublicKey,"PB10203S60738","")
    * match response.ErrorCode == 0

    Given path 'api/SecureChannel/SessionRegister'
    * def requestBody = requestFile.sessionRegister
    Given request requestBody
    * def sessionRegisterEncData = scr.generateSessionRegisterEncData()
    * requestBody.EncData = sessionRegisterEncData
    When method POST
    * scr.decryptData(response.EncData)
    * match response.ErrorCode == 0

    # 将对象存入全局作用域
    * karate.scope.scr = scr
  }

这样,只有第一个场景会执行服务调用和初始化,后续场景直接复用全局的scr对象。


内容的提问来源于stack exchange,提问作者Hakan Aras

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 07:44:51