我正在使用 TestNg 进行测试自动化。我想仅当某个标志为 true 时才启用测试。
例如
布尔值 isEnabled = true;
看到@Test的启用属性无法实现,因为它不接受变量。
而且我不想使用 SkipException
我尝试使用一些监听器,但没有成功。
您应该能够构建一个利用
org.testng.IMethodInterceptor
的 TestNG 侦听器并完成此任务。
一些事实:
org.testng.IMethodInterceptor
的 TestNG 侦听器,在测试方法开始运行之前由 TestNG 调用,其中,您可以从执行计划中添加/删除一个或多个 @Test
方法。org.testng.IAlterSuiteListener
的 TestNG 侦听器,它允许您修改已解析的 <suite>
标签,并且您可以在其中添加/修改 <test>
标签。遵循的步骤:
IMethodInterceptor
和 IAlterSuiteListener
IAlterSuiteListener
的实现基本上会查询系统属性(通过 -D
JVM 参数传递的属性)并查找名为 exclude_methods
的系统属性,如果找到此属性,那么它将更改所有 <suite>
标签并以编程方式向其添加名为 <parameter>
的 exclude_methods
。IMethodInterceptor
的实现基本上会解析<suite>
参数并查找名为exclude_methods
的参数,如果找到,它会将其拆分为n
值(因为我们将允许该值是逗号分隔),对于每个分割值,它将对正则表达式知道的任何元字符进行转义序列,以及它尝试与输入排除列表进行模式匹配的转义序列方法名称,从而修剪方法列表。您可以使用以下机制之一连接此侦听器:
@Listener
注释(或)<listener>
标签(或)有关 TestNG 监听器接线的更多详细信息,请参阅官方文档。
要了解有关方法拦截器的更多信息,请参阅此处。
注意: 确保您使用的是最新发布的 TestNG 版本
v7.10.2
import org.testng.*;
import org.testng.xml.XmlSuite;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class SimpleMethodSelector implements IMethodInterceptor, IAlterSuiteListener {
private static final List<Pattern> patterns = new ArrayList<>();
public static final String EXCLUDE_METHODS = "exclude_methods";
@Override
public void alter(List<XmlSuite> suites) {
//Let's check if there were any methods provided to us to be ignored via the JVM argument
String excludeMethods = System.getProperty(EXCLUDE_METHODS, "");
if (!excludeMethods.isEmpty()) {
//If there were some patterns (can be comma separated) then lets add this
//as a parameter into the <suite> tag.
suites.forEach(it -> it.getParameters().put(EXCLUDE_METHODS, excludeMethods));
}
}
@Override
public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
buildExclusionPatterns(context);
if (patterns.isEmpty()) {
//If there were no patterns to be considered, then it means that the user wants to execute everything
return methods;
}
return methods.stream()
.filter(it -> !shouldExcludeMethod(it.getMethod()))
.collect(Collectors.toList());
}
private static boolean shouldExcludeMethod(ITestNGMethod itm) {
return patterns.stream()
.anyMatch(
pattern -> methodNamesToConsider(itm)
.anyMatch(name -> pattern.matcher(name).matches())
);
}
private static Stream<String> methodNamesToConsider(ITestNGMethod itm) {
return Stream.of(
itm.getQualifiedName(),//Match fully qualified method name (or)
itm.getMethodName() //Match just method name without fully qualified class name
);
}
private static void buildExclusionPatterns(ITestContext context) {
if (!patterns.isEmpty()) {
//If we already built the pattern once, then lets not doing again and again
//because this method will be called for each and every test method within a <test> tag
return;
}
//Check if the current <test> tag contains the parameter "exclude_methods"
String excludedMethods = context.getCurrentXmlTest().getParameter(EXCLUDE_METHODS);
if (Optional.ofNullable(excludedMethods).orElse("").isEmpty()) {
//If there were no methods to be excluded, then there's nothing to process.
//Lets return
return;
}
for (String method : excludedMethods.split(",")) {
String escapedMethod = escape(method); //Escape sequence all the regex special characters
System.err.println("Working with pattern " + escapedMethod);
Pattern pattern = Pattern.compile(escapedMethod, Pattern.DOTALL);
patterns.add(pattern);
}
}
private static String escape(String text) {
return text.chars()
.mapToObj(SimpleMethodSelector::asString)
.collect(Collectors.joining());
}
//Refer https://jenkov.com/tutorials/java-regex/index.html#metacharacters for more details
private static final List<Character> META_CHARACTERS = List.of(
'<', '>', '(', ')', '[', ']', '{', '}', '\\', '^', '-', '=',
'$', '!', '|', '?', '*', '+', '.'
);
private static String asString(int it) {
return META_CHARACTERS.contains((char) it) ? "\\\\" + (char) it : Character.toString(it);
}
}
示例测试用例如下所示:
import org.testng.Reporter;
import org.testng.annotations.Test;
public class SampleTestCase {
@Test
public void a() {
System.err.println(Reporter.getCurrentTestResult().getMethod().getQualifiedName() + "()");
}
@Test
public void b() {
System.err.println(Reporter.getCurrentTestResult().getMethod().getQualifiedName() + "()");
}
@Test
public void c() {
System.err.println(Reporter.getCurrentTestResult().getMethod().getQualifiedName() + "()");
}
}
这是一个示例套件文件
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="78547470_suite" verbose="2">
<parameter name="exclude_methods" value=""/>
<test name="78547470_test">
<classes>
<class name="com.rationaleemotions.so.qn78547470.SampleTestCase"/>
</classes>
</test>
</suite>
这是通过 IntelliJ 运行此测试时的运行配置
这是执行输出
...
... TestNG 7.10.2 by Cédric Beust ([email protected])
...
Working with pattern \\*b\\*
com.rationaleemotions.so.qn78547470.SampleTestCase.a()
com.rationaleemotions.so.qn78547470.SampleTestCase.c()
PASSED: com.rationaleemotions.so.qn78547470.SampleTestCase.c
PASSED: com.rationaleemotions.so.qn78547470.SampleTestCase.a
===============================================
78547470_test
Tests run: 3, Failures: 0, Skips: 0
===============================================