背景
很多测试框架都带有数据驱动的方式,如TestNG
的 @DataProvider
,在合理使用下可以大大减少代码量,同时也便于后期维护。而熟悉TestNG
的同学应该都知道,使用TestNG
的 @DataProvider
时需要先写一个数据源,再 @Test(dataProvider=“xx”)
中引用数据源。
而慢慢看到有不少用例用的数据源,QA是写在Excel等文件里面,这时就觉得直接用原生的略微麻烦了,为啥不能把数据源直接与注解的方式写在测试用例上呢?如下:
@OTPDataProvider(dataFile = "src/test/resources/excel/demo1.xlsx",sheetName = "sheet1")
@Test
public void excelData(JSONObject jsonObject) {
log.info(jsonObject.toJSONString());
}
TestNG 监听器 — IAnnotationTransformer2
为了实现上面的效果,查阅TestNG官方文档,发现有个 IAnnotationTransformer2
接口, IAnnotationTransformer2
接口继承了 IAnnotationTransformer
,而IAnnotationTransformer
接口提供了 public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod)
方法,从方法说明上看这个接口可以在@Test
用例被调用前操作相应的注解。
基于这个监听器的能力,那就自然会想到,如果在 @Test 用例执行时,动态的生成@DataProvider
,并 @Test
上动态添加 dataProvider
属性不就可以。
代码实现
实现 transform()
核心代码其实就2行:
// 动态设定 dataProvider = “xxxx”annotation.setDataProvider(OTPDataProvider.NAME);
// 动态设定 @DataProvider 的classannotation.setDataProviderClass(ExcelDataProvider.class);
/**
* 从 IAnnotationTransformer 接口继承 操作 @Test
*
* @param annotation
* @param testClass
* @param testConstructor
* @param testMethod
*/
@Override
public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
boolean annotationDataProvider = testMethod.isAnnotationPresent(OTPDataProvider.class);
if (annotationDataProvider) {
annotation.setDataProvider(OTPDataProvider.NAME);
DataProviderUtil.chooseData(annotation, testMethod);
}
// 重跑
IRetryAnalyzer retry = annotation.getRetryAnalyzer();
if (retry == null) {
annotation.setRetryAnalyzer(TestNGRetry.class);
}
}
public void chooseData(ITestAnnotation annotation, Method method) {
OTPDataProvider otpDataProvider = method.getAnnotation(OTPDataProvider.class);
String file = otpDataProvider.dataFile().toLowerCase();
if (file.endsWith(JSON_TYPE)) {
annotation.setDataProviderClass(JsonDataProvicer.class);
} else if (!"".equals(otpDataProvider.sqlQuery())) {
annotation.setDataProviderClass(SqlDataProvider.class);
} else if (file.endsWith(TEXT_TYPE)) {
annotation.setDataProviderClass(TxtDataProvider.class);
} else if (file.endsWith(SUPER_EXCEL_TYPE) || file.endsWith(EXCEL_TYPE)) {
// excel
annotation.setDataProviderClass(ExcelDataProvider.class);
}else {
log.error("OTPDataProvider 属性值有误 请认真检查!");
}
}
@DataProvider(name = OTPDataProvider.NAME)
public Iterator<Object[]> dataJson(Class testClass, ITestNGMethod method, ITestContext c) {
OTPDataProvider otpDataProvider = DataProviderUtil.getDataProvderAn(testClass, method);
String dataFile = otpDataProvider.dataFile();
String sheetName = otpDataProvider.sheetName();
return ExcelDataProvider.getData(dataFile, sheetName);
}