fix(接口测试): 引用原场景参数,变量优先级有误

--bug=1039759 --user=陈建星 【场景】-引用的场景配置使用原场景参数并优先原场景参数时,当前场景步骤参数优先级高于原场景参数 https://www.tapd.cn/55049933/s/1502253
This commit is contained in:
AgAngle 2024-04-21 13:35:47 +08:00 committed by Craftsman
parent 820862bcaf
commit da8d506311
6 changed files with 93 additions and 69 deletions

View File

@ -1,61 +1,63 @@
package io.metersphere.api.parser.jmeter;
import io.metersphere.api.parser.jmeter.constants.JmeterAlias;
import io.metersphere.jmeter.mock.Mock;
import io.metersphere.project.api.KeyValueParam;
import io.metersphere.project.dto.environment.variables.CommonVariables;
import org.apache.commons.lang3.BooleanUtils;
import io.metersphere.sdk.util.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Argument;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.modifiers.UserParameters;
import org.apache.jmeter.testelement.TestElement;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.ARGUMENTS_PANEL;
/**
* @Author: jianxing
* @CreateTime: 2024-03-05 21:06
*/
public class JmeterTestElementParserHelper {
public static Arguments getArguments(String name) {
Arguments arguments = new Arguments();
arguments.setEnabled(true);
arguments.setName(name + "_Arguments");
arguments.setProperty(TestElement.TEST_CLASS, Arguments.class.getName());
arguments.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(ARGUMENTS_PANEL));
public static UserParameters getUserParameters(String name) {
UserParameters userParameters = new UserParameters();
userParameters.setEnabled(true);
userParameters.setName(name + "_User Parameters");
userParameters.setPerIteration(true);
userParameters.setProperty(TestElement.TEST_CLASS, UserParameters.class.getName());
userParameters.setProperty(TestElement.GUI_CLASS, JmeterAlias.USER_PARAMETERS_GUI);
return userParameters;
}
public static UserParameters getUserParameters(String name, List<? extends KeyValueParam> argumentList) {
UserParameters arguments = getUserParameters(name);
List<String> names = new LinkedList<>();
List<Object> values = new LinkedList<>();
List<Object> threadValues = new LinkedList<>();
for (int i = 0; i < argumentList.size(); ++i) {
String value = argumentList.get(i).getValue();
String key = argumentList.get(i).getKey();
names.add(key);
values.add(Mock.buildFunctionCallString(value).replaceAll("[\r\n]", ""));
}
arguments.setNames(names);
threadValues.add(values);
arguments.setThreadLists(threadValues);
return arguments;
}
public static Arguments getArguments(String name, List<CommonVariables> argumentList) {
Arguments arguments = getArguments(name);
parse2ArgumentList(argumentList).forEach(arguments::addArgument);
return arguments;
}
public static List<Argument> parse2ArgumentList(List<CommonVariables> variables) {
List<Argument> arguments = new ArrayList<>(variables.size());
variables.forEach(variable -> {
if (BooleanUtils.isFalse(variable.getEnable())) {
return;
}
if (variable.isConstantValid()) {
// 处理常量
String value = StringUtils.isBlank(variable.getValue()) ? variable.getValue()
: Mock.buildFunctionCallString(variable.getValue()).replaceAll("[\r\n]", "");
arguments.add(new Argument(variable.getKey(), value, "="));
} else if (variable.isListValid()) {
// 处理 List 变量
String[] arrays = variable.getValue().replaceAll("[\r\n]", "").split(",");
public static UserParameters getUserParameters(List<CommonVariables> constantVariables, List<CommonVariables> listVariables) {
List<CommonVariables> variableResults = new ArrayList<>();
listVariables.forEach(listVariable -> {
String[] arrays = listVariable.getValue().replaceAll("[\r\n]", "").split(",");
for (int i = 0; i < arrays.length; i++) {
arguments.add(new Argument(variable.getKey() + "_" + (i + 1), arrays[i], "="));
}
CommonVariables commonVariables = BeanUtils.copyBean(new CommonVariables(), listVariable);
commonVariables.setKey(listVariable.getKey() + "_" + (i + 1));
commonVariables.setValue(arrays[i]);
variableResults.add(commonVariables);
}
});
return arguments;
variableResults.addAll(constantVariables);
return getUserParameters(StringUtils.EMPTY, variableResults);
}
}

View File

@ -4,9 +4,6 @@ package io.metersphere.api.parser.jmeter;
import io.metersphere.api.constants.ApiConstants;
import io.metersphere.api.dto.ApiParamConfig;
import io.metersphere.api.dto.request.http.*;
import io.metersphere.project.dto.environment.auth.BasicAuth;
import io.metersphere.project.dto.environment.auth.DigestAuth;
import io.metersphere.project.dto.environment.auth.HTTPAuthConfig;
import io.metersphere.api.dto.request.http.body.Body;
import io.metersphere.api.parser.jmeter.body.MsBodyConverter;
import io.metersphere.api.parser.jmeter.body.MsBodyConverterFactory;
@ -19,6 +16,9 @@ import io.metersphere.project.api.KeyValueEnableParam;
import io.metersphere.project.api.KeyValueParam;
import io.metersphere.project.dto.environment.EnvironmentInfoDTO;
import io.metersphere.project.dto.environment.GlobalParams;
import io.metersphere.project.dto.environment.auth.BasicAuth;
import io.metersphere.project.dto.environment.auth.DigestAuth;
import io.metersphere.project.dto.environment.auth.HTTPAuthConfig;
import io.metersphere.project.dto.environment.host.Host;
import io.metersphere.project.dto.environment.http.HttpConfig;
import io.metersphere.project.dto.environment.http.HttpConfigPathMatchRule;
@ -29,8 +29,11 @@ import io.metersphere.sdk.util.LogUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.http.control.*;
import org.apache.jmeter.modifiers.UserParameters;
import org.apache.jmeter.protocol.http.control.AuthManager;
import org.apache.jmeter.protocol.http.control.Authorization;
import org.apache.jmeter.protocol.http.control.DNSCacheManager;
import org.apache.jmeter.protocol.http.control.HeaderManager;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
@ -89,8 +92,8 @@ public class MsHTTPElementConverter extends AbstractJmeterElementConverter<MsHTT
HashTree httpTree = tree.add(sampler);
// 处理环境变量
Arguments envArguments = getEnvArguments(msHTTPElement, envConfig);
Optional.ofNullable(envArguments).ifPresent(httpTree::add);
UserParameters userParameters = getEnvUserParameters(msHTTPElement, envConfig);
Optional.ofNullable(userParameters).ifPresent(httpTree::add);
// 处理请求头
HeaderManager httpHeader = getHttpHeader(msHTTPElement, apiParamConfig, httpConfig);
@ -190,7 +193,7 @@ public class MsHTTPElementConverter extends AbstractJmeterElementConverter<MsHTT
* @param msHTTPElement
* @param envInfo
*/
private Arguments getEnvArguments(MsHTTPElement msHTTPElement, EnvironmentInfoDTO envInfo) {
private UserParameters getEnvUserParameters(MsHTTPElement msHTTPElement, EnvironmentInfoDTO envInfo) {
if (envInfo == null) {
return null;
}
@ -200,7 +203,7 @@ public class MsHTTPElementConverter extends AbstractJmeterElementConverter<MsHTT
return null;
}
return JmeterTestElementParserHelper.getArguments(msHTTPElement.getName(), envVariables);
return JmeterTestElementParserHelper.getUserParameters(msHTTPElement.getName(), envVariables);
}
private String getPath(MsHTTPElement msHTTPElement, HttpConfig httpConfig) {

View File

@ -8,6 +8,7 @@ import io.metersphere.api.dto.request.processors.MsProcessorConfig;
import io.metersphere.api.dto.scenario.ScenarioConfig;
import io.metersphere.api.dto.scenario.ScenarioStepConfig;
import io.metersphere.api.dto.scenario.ScenarioVariable;
import io.metersphere.api.parser.jmeter.constants.JmeterAlias;
import io.metersphere.api.parser.jmeter.processor.MsProcessorConverter;
import io.metersphere.api.parser.jmeter.processor.MsProcessorConverterFactory;
import io.metersphere.api.parser.jmeter.processor.assertion.AssertionConverterFactory;
@ -23,7 +24,8 @@ import io.metersphere.sdk.util.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.control.CriticalSectionController;
import org.apache.jmeter.modifiers.UserParameters;
import org.apache.jmeter.protocol.http.control.CookieManager;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
@ -32,6 +34,7 @@ import org.apache.jorphan.collections.HashTree;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@ -52,13 +55,18 @@ public class MsScenarioConverter extends AbstractJmeterElementConverter<MsScenar
ApiScenarioParamConfig config = getEnableConfig(msScenario, (ApiScenarioParamConfig) msParameter);
EnvironmentInfoDTO envInfo = config.getEnvConfig(msScenario.getProjectId());
if (isRef(msScenario.getRefType())) {
// 引用的场景中可能包含变量场景包一层临界控制器解决变量冲突
tree = addCriticalSectionController(tree, msScenario);
}
if (isRootScenario(msScenario.getRefType()) && msScenario.getScenarioConfig().getOtherConfig().getEnableGlobalCookie()) {
// 根场景设置共享cookie
tree.add(getCookieManager());
}
// 添加场景和环境变量
addArguments(tree, msScenario, envInfo, config);
addUserParameters(tree, msScenario, envInfo, config);
// 添加环境的前置
addEnvScenarioProcessor(tree, msScenario, config, envInfo, true);
@ -77,6 +85,24 @@ public class MsScenarioConverter extends AbstractJmeterElementConverter<MsScenar
addScenarioAssertions(tree, msScenario, config);
}
/**
* 添加临界控制器解决变量冲突
* @param tree
* @param msScenario
* @return
*/
public HashTree addCriticalSectionController(HashTree tree, MsScenario msScenario) {
String name = msScenario.getName();
boolean enable = msScenario.getEnable();
CriticalSectionController criticalSectionController = new CriticalSectionController();
criticalSectionController.setName(StringUtils.isNotEmpty(name) ? "Csc_" + name : "Scenario Critical Section Controller");
criticalSectionController.setLockName(UUID.randomUUID().toString());
criticalSectionController.setEnabled(enable);
criticalSectionController.setProperty(TestElement.TEST_CLASS, CriticalSectionController.class.getName());
criticalSectionController.setProperty(TestElement.GUI_CLASS, JmeterAlias.CRITICAL_SECTION_CONTROLLER_GUI);
return tree.add(criticalSectionController);
}
/**
* 添加场景和环境变量
*
@ -84,7 +110,7 @@ public class MsScenarioConverter extends AbstractJmeterElementConverter<MsScenar
* @param msScenario
* @param envInfo
*/
private void addArguments(HashTree tree, MsScenario msScenario, EnvironmentInfoDTO envInfo, ApiScenarioParamConfig config) {
private void addUserParameters(HashTree tree, MsScenario msScenario, EnvironmentInfoDTO envInfo, ApiScenarioParamConfig config) {
// 如果是根场景获取场景变量
List<CommonVariables> commonVariables = getCommonVariables(msScenario.getScenarioConfig());
@ -132,9 +158,8 @@ public class MsScenarioConverter extends AbstractJmeterElementConverter<MsScenar
return;
}
Arguments arguments = JmeterTestElementParserHelper.getArguments(msScenario.getName());
JmeterTestElementParserHelper.parse2ArgumentList(constantVariables).forEach(arguments::addArgument);
JmeterTestElementParserHelper.parse2ArgumentList(listVariables).forEach(arguments::addArgument);
UserParameters arguments = JmeterTestElementParserHelper.getUserParameters(constantVariables, listVariables);
tree.add(arguments);
}

View File

@ -23,4 +23,5 @@ public class JmeterAlias {
public static final String ARGUMENTS_PANEL = "ArgumentsPanel";
public static final String BEAN_SHELL_ASSERTION_GUI = "BeanShellAssertionGui";
public static final String BEAN_SHELL_SAMPLER_GUI = "BeanShellSamplerGui";
public static final String CRITICAL_SECTION_CONTROLLER_GUI = "CriticalSectionControllerGui";
}

View File

@ -4,7 +4,6 @@ import io.metersphere.api.dto.ApiParamConfig;
import io.metersphere.api.parser.jmeter.JmeterTestElementParserHelper;
import io.metersphere.api.parser.jmeter.constants.JmeterAlias;
import io.metersphere.api.parser.jmeter.constants.JmeterProperty;
import io.metersphere.jmeter.mock.Mock;
import io.metersphere.plugin.api.constants.ElementProperty;
import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.project.api.KeyValueParam;
@ -15,7 +14,7 @@ import io.metersphere.project.dto.environment.EnvironmentInfoDTO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.modifiers.UserParameters;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
@ -72,7 +71,7 @@ public abstract class ScriptProcessorConverter extends MsProcessorConverter<Scri
testElement.setProperty(JmeterProperty.SCRIPT_LANGUAGE, scriptLanguage.toLowerCase());
}
public static Arguments getScriptArguments(ScriptProcessor scriptProcessor) {
public static UserParameters getScriptArguments(ScriptProcessor scriptProcessor) {
if (scriptProcessor == null || !scriptProcessor.isEnableCommonScript() || !scriptProcessor.isValid()) {
return null;
}
@ -91,11 +90,7 @@ public abstract class ScriptProcessorConverter extends MsProcessorConverter<Scri
return null;
}
Arguments arguments = JmeterTestElementParserHelper.getArguments(scriptProcessor.getName());
for (KeyValueParam param : params) {
arguments.addArgument(param.getKey(), Mock.buildFunctionCallString(param.getValue()), "=");
}
return arguments;
return JmeterTestElementParserHelper.getUserParameters(scriptProcessor.getName(), params);
}
public static boolean isJSR233(ScriptProcessor scriptProcessor) {

View File

@ -2,6 +2,7 @@ package io.metersphere.project.dto.environment.variables;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.metersphere.project.api.KeyValueParam;
import io.metersphere.sdk.constants.VariableTypeConstants;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@ -11,17 +12,13 @@ import java.io.Serializable;
import java.util.List;
@Data
public class CommonVariables implements Serializable {
public class CommonVariables extends KeyValueParam implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "id")
private String id;
@Schema(description = "变量名")
private String key;
@Schema(description = "变量类型 CONSTANT LIST JSON")
private String paramType = VariableTypeConstants.CONSTANT.name();
@Schema(description = "变量值")
private String value;
@Schema(description = "状态")
private Boolean enable = true;
@Schema(description = "描述")
@ -32,17 +29,18 @@ public class CommonVariables implements Serializable {
@JsonIgnore
public boolean isConstantValid() {
return StringUtils.equals(this.paramType, VariableTypeConstants.CONSTANT.name()) && StringUtils.isNotEmpty(key);
return (StringUtils.equals(this.paramType, VariableTypeConstants.CONSTANT.name()) || StringUtils.isBlank(paramType))
&& isValid();
}
@JsonIgnore
public boolean isListValid() {
return StringUtils.equals(this.paramType, VariableTypeConstants.LIST.name()) && StringUtils.isNotEmpty(key) && StringUtils.isNotEmpty(value) && value.indexOf(",") != -1;
return StringUtils.equals(this.paramType, VariableTypeConstants.LIST.name()) && isValid() && isNotBlankValue() && getValue().indexOf(",") != -1;
}
@JsonIgnore
public boolean isJsonValid() {
return StringUtils.equals(this.paramType, VariableTypeConstants.JSON.name()) && StringUtils.isNotEmpty(key);
return StringUtils.equals(this.paramType, VariableTypeConstants.JSON.name()) && isValid();
}
}