Using TestNG DataProviders under Junit4

A DataProvider allows you to declare a list of resources to be passed to unit tests. These tests are played for each element of this list (this avoids coding a "for"). This functionality, coming from TestNG, can be ported to JUnit4 via the library https://github.com/TNG/junit-dataprovider. And it is Spring Framework compatible.

The value of the DataProvider

What is a DataProvider

A DataProvider allows a unit test to be launched by providing it with input data, this data taking the form of parameters to the unit test method. For example, it is possible to define a list of first names and surnames to a method testing an enrolment.

A DataProvider is of course reusable.

Finally, it is presented in the form of a one- or two-dimensional table to associate several data with each test run, for example a list of first and last names.

An example of a DataProvider will speak for itself:

@DataProvider
public static Object[][] dataProviderPeople() {
    return new Object[][]{
            {"John", "Karmac"},
            {"Bob", "Dylan"},
            {"Dexter", "Holland"}
    };
}

Here we have a DataProvider declaring three first name-last name pairs.

Here's how to use it:

@Test
@UseDataProvider(value = "dataProviderPeople")
public void shouldRegister(String name, String surname) throws Exception {

This test will be run 3 times, with the parameters "name" and "surname" being John Karmac, Bob Dylan, and then Dexter Holland respectively.

Testing datasets without DataProvider

Without a DataProvider, we would have been tempted to define the dataset somewhere and then declare a "for" loop (or a "foreach" in Java 8) in the unit test, so that the test would be run only once.

A problem arises: if one of the iterations fails the test, you will not immediately know which one. You will have to run the test with a debugger or look at logs. In short, you will lose time.

Improving tests with DataProviders

With the DataProvider seen above, the test method will be executed 3 times. In case of failure, we will know which first name and last name pair is at fault.

With this implementation of the :

@Test
@UseDataProvider(value = "dataProviderPeople")
public void shouldRegister(String name, String surname) throws Exception {
    if (name.equalsIgnoreCase("BOB")) {
        fail("pour l'article");
    }
}

Here is an example of execution from IntelliJ IDEA :

execution from IntelliJ IDEA

 

The faulty data are clearly identified.

For information, this is the presentation of the data that we would have had with TestNG.

How to use a DataProvider in JUnit4

Install junit-dataprovider

Declare dependency on "com.tngtech.java:junit-dataprovider".

Maven example:

<dependencies>
	<!-- ... -->
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.12</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>com.tngtech.java</groupId>
		<artifactId>junit-dataprovider</artifactId>
		<version>1.12.0</version>
		<scope>test</scope>
	</dependency>
	<!-- ... -->
</dependencies>

Example Gradle :

repositories {
    mavenCentral()
}
dependencies {
    // …
    testCompile
group: ‘junit’, name: ‘junit’, version: ‘4.12’
    testCompile
group: ‘com.tngtech.java’, name: ‘junit-dataprovider’, version: ‘1.12.0’
    

}

(These examples are taken from https://github.com/TNG/junit-dataprovider/wiki/Getting-started#download)

Declaring and using a DataProvider

A DataProvider allows you to define from 1 to n values on each line. Example:

@DataProvider
public static Object[][] dataProviderUnNom() {
    return new Object[][]{
            {val_1a, val_1b, val_1c,},
            {val_2a, val_2b, val_2c,},};
}

The DataProvider can be named in several ways:

  • Name it in the same way as a unit test so that it is automatically associated with it (see https://github.com/TNG/junit-dataprovider/wiki/Features#convention-over-configuration for explanations)
  • Name it as you wish. The DataProvider can be associated with all unit tests with the appropriate annotation: @UseDataProvider(value = "dataprovider name")
  • The https://github.com/TNG/junit-dataprovide r/wiki/Features#customize-test-method-name page offers many other ways to name and associate your DataProviders

Finally, you can insert objects of any type. For example, you can pass a "String" name and an "int" age. The DataProvider will declare an array of "Objects", and the unit tests will declare "String" and "int" parameters.

A limitation to be aware of

Be careful with the data you place in your DataProviders. Indeed, their initialization occurs before the invocation of the @Before methods of JUnit. You will therefore not be able to directly use data generated in an @Before phase.

Running JUnit tests

To be compatible with DataProviders, your test classes must use the appropriate runner:

@RunWith(DataProviderRunner.class)

How to use a DataProvider with a Spring Runner in JUnit4

You probably run your test classes with a Spring runner such as "SpringJUnit4ClassRunner", and you know that JUnit doesn't allow you to use two at the same time.

The developers of junit-dataprovider provide a custom runner that combines the functionality of both the "SpringJUnit4ClassRunner" and "DataProviderRunner" runners. Copy the DataProviderRunnerWithSpring.java class into your project and simply use the "DataProviderRunnerWithSpring" runner.

Example:

@RunWith(DataProviderRunnerWithSpring.class)
@SpringBootTest(classes = Application.class, webEnvironment = RANDOM_PORT)
public class SampleTest {

I was able to test it successfully on a recent Spring Boot stack (1.5.x) with most of the usual modules (Boot, Web, WS, Security, Data, Batch) both on the command line and with IntelliJ IDEA 2016.3.5.

 

Are you interested in this topic?

CONTACT US