Skip to content

Tips and Tricks

Andreas Schmid edited this page Nov 11, 2017 · 15 revisions

JUnit4 DataProvider for Dependency Injection Frameworks

To use the junit-dataprovider's @DataProvider in integration tests you need to extend the runners that manage the containers.

JUnit4 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 junit-dataprovider v1.10.0 and cdi-unit v3.1.3. You are welcome to report working/non-working combinations.

JUnit4 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 {
    /* ... */
}

Since v1.10.4

This is all what is required, see example integration test in package com.tngtech.test.java.junit.dataprovider.spring.

Before v1.10.4

The body of this class is identical with DataProviderRunner.java but needs a small hack to work for version v1.10.3 and smaller. 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.