如何让Karate Feature中初始化的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,确保初始化只执行一次。
- 创建
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
- 修改
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; }
- 在原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

