背景

很多测试框架都带有数据驱动的方式,如TestNG@DataProvider ,在合理使用下可以大大减少代码量,同时也便于后期维护。而熟悉TestNG的同学应该都知道,使用TestNG@DataProvider 时需要先写一个数据源,再 @Test(dataProvider=“xx”) 中引用数据源。
而慢慢看到有不少用例用的数据源,QA是写在Excel等文件里面,这时就觉得直接用原生的略微麻烦了,为啥不能把数据源直接与注解的方式写在测试用例上呢?如下:

  1. @OTPDataProvider(dataFile = "src/test/resources/excel/demo1.xlsx",sheetName = "sheet1")
  2. @Test
  3. public void excelData(JSONObject jsonObject) {
  4. log.info(jsonObject.toJSONString());
  5. }

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 的class
annotation.setDataProviderClass(ExcelDataProvider.class);

  1. /**
  2. * 从 IAnnotationTransformer 接口继承 操作 @Test
  3. *
  4. * @param annotation
  5. * @param testClass
  6. * @param testConstructor
  7. * @param testMethod
  8. */
  9. @Override
  10. public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
  11. boolean annotationDataProvider = testMethod.isAnnotationPresent(OTPDataProvider.class);
  12. if (annotationDataProvider) {
  13. annotation.setDataProvider(OTPDataProvider.NAME);
  14. DataProviderUtil.chooseData(annotation, testMethod);
  15. }
  16. // 重跑
  17. IRetryAnalyzer retry = annotation.getRetryAnalyzer();
  18. if (retry == null) {
  19. annotation.setRetryAnalyzer(TestNGRetry.class);
  20. }
  21. }
  22. public void chooseData(ITestAnnotation annotation, Method method) {
  23. OTPDataProvider otpDataProvider = method.getAnnotation(OTPDataProvider.class);
  24. String file = otpDataProvider.dataFile().toLowerCase();
  25. if (file.endsWith(JSON_TYPE)) {
  26. annotation.setDataProviderClass(JsonDataProvicer.class);
  27. } else if (!"".equals(otpDataProvider.sqlQuery())) {
  28. annotation.setDataProviderClass(SqlDataProvider.class);
  29. } else if (file.endsWith(TEXT_TYPE)) {
  30. annotation.setDataProviderClass(TxtDataProvider.class);
  31. } else if (file.endsWith(SUPER_EXCEL_TYPE) || file.endsWith(EXCEL_TYPE)) {
  32. // excel
  33. annotation.setDataProviderClass(ExcelDataProvider.class);
  34. }else {
  35. log.error("OTPDataProvider 属性值有误 请认真检查!");
  36. }
  37. }
    @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);
    }

效果展示

image.png