常见的TestNG玩法,在网络上随意可见,今天在Istanbul团队里发现了他们的新玩法: Beanshell
先贴下beanshell在xml的配置示例,它可以通过一段脚本或者代码来自定义需要执行的测试用例,非常灵活。
TestNG里的Beanshell长什么样子?
先解释下这里beanshell做了什么,它先导入(import)了一个工具包,然后调用了这个工具包里的方法TestToExecuteMethodSelector.check(method, testngMethod); 其中这两个参数,根据官网上的解释:
- java.lang.reflect.Method method: the current test method.
- org.testng.ITestNGMethod testngMethod: the description of the current test method.
如何自定义实现Beanshell
到这里,概念还有点抽象,可以具体看看上面Beanshell代码TestToExecuteMethodSelector.check(method, testngMethod)做了什么?
public class TestToExecuteMethodSelector {
private static LogManager logger = new LogManager();
public static boolean check(final Method method, final ITestNGMethod testngMethod) throws IOException {
if (method.isAnnotationPresent(AutomatedTest.class)) {
AutomatedTest automatedTestAnnotation = method.getAnnotation(AutomatedTest.class);
return selector(automatedTestAnnotation.workItemId(), method, testngMethod);
} else {
return false;
}
}
private static boolean selector(final int Id, final Method method, final ITestNGMethod testngMethod)
throws IOException {
final List workItemIdList = new ArrayList();
final ClassPathResource resource = new ClassPathResource("testToRun");
final InputStream inputStream = resource.getInputStream();
final InputStreamReader streamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
final BufferedReader reader = new BufferedReader(streamReader);
for (String line; (line = reader.readLine()) != null;) {
if (line.trim() != "" && line.trim() != null) {
try {
workItemIdList.add(Integer.parseInt(line.trim()));
} catch (NumberFormatException e) {
logger.logInfo("Unknown character found in testToRun text file.");
} catch (Exception e) {
logger.logInfo("An exception occured while parsing line to number.");
e.printStackTrace();
}
}
}
return workItemIdList.contains(Id) && testngMethod.getEnabled();
}
}
代码里有testToRun的resource文件,以及AutomatedTest这个注解。
为了更好的理解,先看看testng的测试用例代码。可以看到,除了@Test,还增加了@AutomatedTest这个自定义注解,其中workItemId表明用例的id号,它可能是Jira里的一个数字。通过method.getAnnotation(AutomatedTest.class),我们可以得到AutomatedTest注解上的workItemId值。
@Test(enabled = false)
@AutomatedTest(state = ImplementationState.IMPLEMENTED, workItemId = 1)
public void testDocumentWithFileType() throws Exception {
//测试用例执行主体:
...
logManager.logTestStepVerify("Return code is SC_CREATED");
...
}
所以,我们可以在testToRun的文件里,定义这个workItemId,以达到灵活执行指定用例的目的。譬如,testToRun可以定义如下,这表示我这次需要执行id为1,23,45的这三个用例。
1
23
45
回到Xml定义的beanshell脚本, 很明显它的整个过程是:TestNG会为你定义好的
return workItemIdList.contains(Id) && testngMethod.getEnabled();
可以看出,虽然1这个用例在testToRun里面定义了,但是由于enabled = false,所以,这个1用例是不会被执行的。
附加
为了故事的完整性,再附上上面代码里提到的AutomatedTest注解。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutomatedTest {
int workItemId();
ImplementationState state();
boolean blocked() default false;
}
其中,ImplementationState是表明这个用例是否已经实现完成可用于执行?还是处于planned状态?
public enum ImplementationState {
PLANNED, IMPLEMENTED
}