0

Does anyone know how TestNG figures out what the data provider method is based on the data provider name specified for a test method?

I found this solution: https://gist.github.com/ae6rt/3805639

However, it doesn't take into account that the data provider could be:

  • defined in a completely different class, or
  • defined in a parent class, and
  • the method can be either static, or not static.

I tried to hack something together on my own, but then I figured that I can't possibly be the first person to try to solve the problem, especially because obviously TestNG must have a solution to that.

Does anyone know how TestNG does it, and how to get access to that business logic?

I'm trying to figure out the "total test count" at start up as discussed here: How to get total amount of tests (incl. taking data providers into account) at TestNG start?

Community
  • 1
  • 1
mac
  • 2,672
  • 4
  • 31
  • 43
  • You should ask an evolution of TestNG itself (even I can't promise it will be done). https://github.com/cbeust/testng/issues – juherr Aug 01 '15 at 19:17
  • What do you mean by "evolution"? I think we have to assume that TestNG is already doing it, right? I was wondering if someone knows how / where the code for it is. – mac Aug 02 '15 at 22:47
  • Nope. As I know it doesn't. Have a look at its sources, it is the best location to have responses. – juherr Aug 03 '15 at 03:21
  • @juherr - Do you think we should add this to TestNG ? – Krishnan Mahadevan Dec 16 '18 at 17:45
  • @KrishnanMahadevan yes, I think TestNG should provide an API which allows to read the testng model. Here, we should be able to find a IDataProvider by its name from an ITestClass. But an ITestMethod should have an `Optionnal getDataProvider()`. – juherr Dec 16 '18 at 20:04
  • @juherr I have created a issue to track this https://github.com/cbeust/testng/issues/1987 – Krishnan Mahadevan Dec 17 '18 at 03:11

1 Answers1

2

This might be a very late answer, but still posting it.

The below samples show how it can be done using TestNG. I am using TestNG 7.0.0-beta1 (latest released version as of today).

Scenario 1 : Data provider resides in the same class.

import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

@Listeners(DataProviderTrackingListener.class)
public class DataProviderInSameClass {

  @Test(dataProvider = "dp")
  public void testMethod(int i) {
    System.err.println(i);
  }

  @DataProvider(name = "dp")
  public Object[][] getData() {
    return new Object[][] {{1}, {2}};
  }
}

Scenario 2 : Data provider resides in a base class.

import org.testng.annotations.DataProvider;

public class BaseClass {
  @DataProvider(name = "dp")
  public Object[][] getData() {
    return new Object[][] {{1}, {2}};
  }
}

import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

@Listeners(DataProviderTrackingListener.class)
public class DataProviderInBaseClass extends BaseClass {

  @Test(dataProvider = "dp")
  public void testMethod(int i) {
    System.err.println(i);
  }
}

Scenario 3 : Data provider resides in a completely different class.

import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

@Listeners(DataProviderTrackingListener.class)
public class DataProviderInDifferentClass {

  @Test(dataProvider = "dp", dataProviderClass = BaseClass.class)
  public void testMethod(int i) {
    System.err.println(i);
  }
}

The listener which extracts all of this information and shows it to you

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Optional;
import org.testng.ITestListener;
import org.testng.ITestResult;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class DataProviderTrackingListener implements ITestListener {

  @Override
  public void onTestStart(ITestResult result) {
    Test test = result.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class);
    if (test == null) {
      return;
    }
    Method method = null;
    Class<?> foundInClass = null;
    if (isDataProviderPresentInDifferentClass(test)) {
      method = extractDataProviderMethodFrom(test.dataProviderClass());
      foundInClass = test.dataProviderClass();
    } else {
      Class<?> currentClass = result.getInstance().getClass();
      while (currentClass != Object.class) {
        Optional<Method> methods =
            Arrays.stream(currentClass.getDeclaredMethods())
                .filter(
                    eachMethod -> {
                      DataProvider dataProvider = eachMethod.getAnnotation(DataProvider.class);
                      return (dataProvider != null
                          && dataProvider.name().equals(test.dataProvider()));
                    })
                .findFirst();
        if (methods.isPresent()) {
          method = methods.get();
          foundInClass = currentClass;
          break;
        }
        currentClass = currentClass.getSuperclass();
      }
    }
    if (method != null) {
      String msg =
          String.format(
              "Data provider [%s] found in class [%s]", method.getName(), foundInClass.getName());
      System.err.println(msg);
    }
  }

  private static boolean isDataProviderPresentInDifferentClass(Test test) {
    return test.dataProviderClass() != Object.class;
  }

  private static Method extractDataProviderMethodFrom(Class<?> clazz) {
    Optional<Method> method =
        Arrays.stream(clazz.getMethods())
            .filter(eachMethod -> eachMethod.getAnnotation(DataProvider.class) != null)
            .findFirst();
    return method.orElse(null);
  }
}
Krishnan Mahadevan
  • 14,121
  • 6
  • 34
  • 66