Skip to content

Tips and Tricks

Johannes Najjar edited this page Nov 2, 2015 · 15 revisions

DataProvider for Dependency Injection Frameworks

To use DataProvider in integration tests you need to extend the runners that manage the containers.

DataProvider for CDI

Simply copy the source code of DataProviderRunner.java into your project and rename it to something like "CdiDataProviderRunner.java" and then replace BlockJUnit4ClassRunner with CdiRunner, i.e., write

public class CdiDataProviderRunner extends CdiRunner

This was tested and works for the basic use cases with DataProvider Version 1.10.0 and cdi-unit.version 3.1.3. You are welcome to report working/non-working combinations.

DataProvider for Spring Integration Testing

To use the junit-dataprovider together with Spring integration testing, i.e. tests which have to run with the SpringJUnit4ClassRunner, you need to create a new JUnit runner similar to

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

public class DataProviderRunnerWithSpring extends SpringJUnit4ClassRunner {
    /* ... */
}

The body of this class is identical with DataProviderRunner.java but needs a small hack to work. Without any modification a NullPointerException is thrown in

  void invokeBeforeClass() {
    Statement statement = new Statement() {
      @Override
      public void evaluate() {
        // nothing to do here
      }
    };
    statement = super.withBeforeClasses(statement);
    try {
      statement.evaluate();
    } catch (Throwable e) {
      failure = e;
    }
  }

The simplest solution is to comment out statement.evaluate();, which obviously breaks the @BeforeClass annotation. Alternatively, replacing the function above by the following two functions also fixes the issue without breaking @BeforeClass.

  void invokeBeforeClass() {
    Statement statement = new Statement() {
      @Override
      public void evaluate() {
        // nothing to do here
      }
    };
    Statement runBeforeTestClassCallbacks = super.withBeforeClasses(statement);

    // ATTENTION: This hack breaks "org.springframework.test.context.support.AbstractTestExecutionListener.beforeTestClass(TestContext)" 
    // as it will never be called
    Statement next = getNextFromRunBeforeTestClassCallbacks(runBeforeTestClassCallbacks);
    try {
      next.evaluate();
    } catch (Throwable e) {
      failure = e;
    }
  }

  private Statement getNextFromRunBeforeTestClassCallbacks(Statement junitBeforeClasses) {
    try {
      Field nextField = junitBeforeClasses.getClass().getDeclaredField("next");
      nextField.setAccessible(true);
      return ((Statement) nextField.get(junitBeforeClasses));
    } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
      throw new RuntimeException("Could not get 'next'-Field from 'runBeforeTestClassCallbacks' statement.", e);
    }
  }

This workaround results in org.springframework.test.context.support.AbstractTestExecutionListener.beforeTestClass(TestContext) to never be called. As the default implementation of this method does not contain any operations, this should be acceptable in most cases.