背景
很多测试框架都带有数据驱动的方式,如TestNG 的 @DataProvider ,在合理使用下可以大大减少代码量,同时也便于后期维护。而熟悉TestNG的同学应该都知道,使用TestNG 的 @DataProvider 时需要先写一个数据源,再 @Test(dataProvider=“xx”) 中引用数据源。
而慢慢看到有不少用例用的数据源,QA是写在Excel等文件里面,这时就觉得直接用原生的略微麻烦了,为啥不能把数据源直接与注解的方式写在测试用例上呢?如下:
@OTPDataProvider(dataFile = "src/test/resources/excel/demo1.xlsx",sheetName = "sheet1")@Testpublic 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*/@Overridepublic 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)) {// excelannotation.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);
}
效果展示

