1

如果我有一个方法:

@Test(dataProvider = "webTarget")
void testFirst(WebTarget target) {
    // ...
}

如果我有一个方法,我可以在 TestNG 中创建一个监听器或其他东西吗:

@Test
void testFirst(WebTarget target) {
    // ...
}

那么它会自动注入特定的dataProvider,而不是明确指定它@Test(dataProvider = "webTarget")吗?

4

2 回答 2

2

理想情况下,最简单的方法是:

  1. 定义一个抽象类,您可以在其中定义所需的数据提供者以及数据提供者将从中获取的数据,作为其源并将其提供给测试方法(这可能类似于数据提供者从注入的值中获取进去)
  2. 拥有您的测试类,扩展这个抽象类,然后从org.testng.IAnnotationTransformer实现中,您只需将数据提供者方法名称注入测试类。

如果您也不想使用抽象类,那么这是另一种选择。这种看起来像是一种迂回的做法。

对于这个例子,我使用的依赖注入框架是 Guice。

我们将在此示例中使用的接口如下

/**
 * Lets any test class expose the injected values to any caller.
 */
public interface ObjectGetter {

  /**
   * @return - The {@link Student} object that is required.
   */
  Student getStudent();
}
/**
 * Allows for setting the actual object to be used by any data provider
 */
public interface ObjectSetter {

  /**
   * @param student - The {@link Student} object
   */
  void setStudent(Student student);
}

这是我们在这个例子中使用的 Guice 模块的样子

import com.google.inject.Binder;
import com.google.inject.Module;

public class MyLocalGuiceModule implements Module {

  @Override
  public void configure(Binder binder) {
    binder.bind(Student.class).toInstance(new Student(100, "KungFu-Panda"));
  }
}

这是测试类的样子

import com.google.inject.Inject;

import static org.assertj.core.api.Assertions.assertThat;

import org.testng.annotations.Guice;
import org.testng.annotations.Test;

@Guice(modules = MyLocalGuiceModule.class)
public class SampleTestClass implements ObjectGetter {

  @Inject
  private Student student;

  @Override
  public Student getStudent() {
    return student;
  }

  @Test
  public void testMethod(Student s) {
    String text = s.toString();
    assertThat(text).isEqualTo("Student{id=100, name='KungFu-Panda'}");
  }
}

下面是单独的数据提供者类的样子

import org.testng.annotations.DataProvider;

public class DataProviderHouse implements ObjectSetter {

  private Student student;

  @DataProvider(name = "students")
  public Object[][] getStudents() {
    return new Object[][]
        {
            {student}
        };
  }

  @Override
  public void setStudent(Student student) {
    this.student = student;
  }
}

注释转换器如下所示:

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.testng.IAnnotationTransformer;
import org.testng.annotations.ITestAnnotation;

public class LocalTransformer implements IAnnotationTransformer {

  @Override
  public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor,
      Method testMethod) {
    annotation.setDataProviderClass(DataProviderHouse.class);
    annotation.setDataProvider("students");
  }
}

数据提供者侦听器如下所示:

import org.testng.IDataProviderListener;
import org.testng.IDataProviderMethod;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;

public class DataProviderListener implements IDataProviderListener {

  @Override
  public void beforeDataProviderExecution(IDataProviderMethod dataProviderMethod,
      ITestNGMethod method, ITestContext iTestContext) {
    Object dpInstance = dataProviderMethod.getInstance();
    if (!(dpInstance instanceof ObjectSetter)) {
      return;
    }
    Object testInstance = method.getInstance();
    if (!(testInstance instanceof ObjectGetter)) {
      return;
    }
    ((ObjectSetter) dpInstance).setStudent(((ObjectGetter) testInstance).getStudent());
  }
}

这是套件 xml 的样子

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="dynamic_data_provider_suite" verbose="2">
  <listeners>
    <listener class-name="com.rationaleemotions.dynamic.LocalTransformer"/>
    <listener class-name="com.rationaleemotions.dynamic.DataProviderListener"/>
  </listeners>
  <test name="dynamic_data_provider_test" verbose="2">
    <classes>
      <class name="com.rationaleemotions.dynamic.SampleTestClass"/>
    </classes>
  </test>
</suite>

以下是预计会发生的一系列事件:

  1. 测试类使用一个 Guice 模块,它将所需的依赖项注入到测试类中。
  2. 测试类通过接口向任何调用者(在本例中为数据提供者监听器)公开注入的依赖项com.rationaleemotions.dynamic.ObjectGetter
  3. 我们有一个实现org.testng.IAnnotationTransformer,我们将数据提供者类和方法引用注入到测试方法中。
  4. 数据提供者类是一个单独的类,com.rationaleemotions.dynamic.ObjectSetter它使用它来获取它应该用于数据驱动测试的数据。
  5. 我们创建了一个实现org.testng.IDataProviderListener,TestNG 提供该实现来窃听数据提供者的调用事件之前和之后。使用这个监听器,我们从测试类中提取 Guice 注入的数据,然后将其注入到数据提供者所属的对象中。

这样做的方法有点长,但更有助于使数据提供者真正动态化。

您的使用里程可能会根据您希望采用这种“复杂但复杂的方法”的实际用例而有所不同。

于 2021-11-28T05:49:52.107 回答
1

如果事先知道每种测试方法的数据提供者方法,则可以使用IAnnotationTransformer. 但是您需要使用 xml 套件文件来运行测试。

注意:(docMap.of)是在 java 9 中引入的。如果您使用的是以前的版本,那么您可以创建using normal hash map。dpNameMap

public class CustomAnnotationTransformer implements IAnnotationTransformer {
    private static final Map<String, String> dpNameMap = Map.of("methodOne", "dataOne",
                                                                "methodTwo", "dataTwo",
                                                                "methodThree", "dataThree");

    @Override
    public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method method) {
        if(method != null && dpNameMap.containsKey(method.getName())) {
            annotation.setDataProvider(dpNameMap.get(method.getName()));
        }
    }

}

还要在您的套件文件中添加以下内容:

<listeners>
    <listener class-name="com.yourpackage.CustomAnnotationTransformer" />
</listeners>
于 2021-11-23T14:35:46.183 回答