Skip to content

Features

Andreas Schmid edited this page Nov 4, 2018 · 50 revisions

**Disclaimer: JUnit5 provides almost all of the features listed below with a little different syntax. For details see Migration guides.


Below you can find code examples for all supported features. Browsing the integration tests you can find even more examples. Please have a look at the corresponding packages for Java and Groovy integration tests.

Array syntax

Since: v1.0.0

This was the first use case for Java and its native array syntax support:

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.RunWith;

import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;

@RunWith(DataProviderRunner.class)
public class DataProviderArraySyntaxTest {

    @DataProvider
    public static Object[][] dataProviderAdd() {
        // @formatter:off
        return new Object[][] {
                { 0, 0, 0 },
                { 1, 1, 2 },
                /* ... */
        };
        // @formatter:on
    }

    @Test
    @UseDataProvider("dataProviderAdd")
    public void testAdd(int a, int b, int expected) {
        // When:
        int result = a + b;

        // Then:
        assertEquals(expected, result);
    }
}

Please note that if you only have a single parameter test method, since v1.10.3 you can also use Object[] instead.

String syntax

Since: v1.7.0 and further customizable since v1.8.0

If you don't want to maintain two methods for the dataprovider and the test method, you can use the String syntax. Here, instead of using @UseDataProvider to point to a method providing test data, you directly pass test data using @DataProvider annotation and its #value() method to provide an array of regex-separated Strings (via #splitBy(); default ","). After splitting, each String value can be trimmed back by spaces (= " "), tabs (= "\t) and line-separator (= "\n" or "\r") (via #trimValues(); default true). The resulting String is then parsed to its corresponding type in the test method signature. All

  • primitive types (e.g. char, boolean, int),
  • primitive wrapper types (e.g. Long, Double),
  • case-sensitive Enum names,
  • Strings, and
  • types having single-argument String constructor are supported. The String "null" will be passed as null object or "null" String according to #convertNulls() (default true).
    // @formatter:off
    @Test
    @DataProvider(value = {
        "               |  0",
        "a              |  1",
        "abc            |  3",
        "veryLongString | 14",
    }, splitBy = "\\|", trimValues = true)
    // @formatter:on
    public void testStringLength2(String str, int expectedLength) {
        // Expect:
        assertThat(str.length()).isEqualTo(expectedLength);
    }

    @Test
    @DataProvider({ "null", "" })
    public void testIsEmptyString2(String str) {
        // When:
        boolean isEmpty = (str == null) ? true : str.isEmpty();

        // Then:
        assertThat(isEmpty).isTrue();
    }

For more information about the @DataProvider options, see DataProvider.java.

You can also use the String syntax in a separate @DataProvider method:

    @DataProvider
    public static String[] dataProviderFileExistence() {
        // @formatter:off
        return new String[] {
                "src,             true",
                "src/main,        true",
                "src/main/java/,  true",
                "src/test/java/,  true",
                "test,            false",
        };
        // @formatter:on
    }

    @Test
    @UseDataProvider("dataProviderFileExistence")
    public void testFileExistence(File file, boolean expected) {
        // Expect:
        assertEquals(expected, file.exists());
    }

This feature is inspired by JUnitParams and @janschaefer.

Iterable syntax

Since: v1.6.0 For other languages on the JVM which have native List support, you can use junit-dataprovider's List syntax.

Note: The below example is Groovy. This only makes sense if you are not able to use the very powerful Spock framework, e.g. see (#12).

Since: v1.10.3 You can also use List<Object> if you only have a single parameter test method.

Since: v1.12.0 You can use any subclass of Iterable instead of only List for the inner and outer type. Be aware, though, that the order of the inner Iterable is important, so Set might not be a good choice. Also Object is no longer necessary as element type. You can use anything as type checking is done once converting to the desired parameter types.

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

import org.junit.Test
import org.junit.runner.RunWith

import com.tngtech.java.junit.dataprovider.*

@RunWith(DataProviderRunner)
class DataProviderTest {

    @DataProvider
    static List<List<Object>> dataProviderBooleanLogicAnd() {
        // @formatter:off
        return [
            [ false,  false,  false ],
            [ true,   false,  false ],
            [ false,  true,   false ],
            [ true,   true,   true ],
        ]
        // @formatter:on
    }

    @Test
    @UseDataProvider('dataProviderBooleanLogicAnd')
    void 'test boolean logic for "and"'(op1, op2, expected) {
        // Expect:
        assert (op1 && op2) == expected
    }
}

Custom dataprovider method resolvers

Since: v1.12.0 The ability to provide custom resolvers to find proper dataprovider methods is added. The current logic was moved to DefaultDataProviderMethodResolver which is the used default implementation. This means the code is fully backwards compatible. See also sections Change @DataProvider location and Convention over configuration for its features. In addition junit-dataprovider is enabled to return multiple dataprovider methods.

    /**
     * {@link DataProviderMethodResolver} which returns all dataproviders which start with the name of the {@code testMethod}.
     */
    private static class DataProviderStartWithTestMethodNameResolver extends DefaultDataProviderMethodResolver {
        @Override
        protected List<FrameworkMethod> findDataProviderMethods(List<TestClass> locations, String testMethodName, String useDataProviderValue) {
            List<FrameworkMethod> result = new ArrayList<FrameworkMethod>();
            for (TestClass location : locations) {
                List<FrameworkMethod> dataProviderMethods = location.getAnnotatedMethods(DataProvider.class);
                for (FrameworkMethod dataProviderMethod : dataProviderMethods) {
                    if (dataProviderMethod.getName().startsWith(testMethodName)) {
                        result.add(dataProviderMethod);
                    }
                }
            }
            return result;
        }
    }

    private static AtomicInteger counter;

    @BeforeClass
    public static void setupClass() {
        counter = new AtomicInteger(0);
    }

    @AfterClass
    public static void tearDownClass() {
        assertThat(counter.get()).isEqualTo(6);
    }

    @DataProvider
    public static Object[][] testNumberOne() {
        // @formatter:off
        return new Object[][] {
            { (byte) 1 },
        };
        // @formatter:on
    }

    @DataProvider
    public static Object[][] testNumberTwo() {
        // @formatter:off
        return new Object[][] {
            { 2 },
            { Integer.valueOf(3) }
        };
        // @formatter:on
    }

    @DataProvider
    public static Set<Number> testNumberThree() {
        Set<Number> result = new LinkedHashSet<Number>();
        result.add(4);
        result.add(5L);
        result.add(6.0);
        return result;
    }

    @Test
    @UseDataProvider(resolver = DataProviderStartWithTestMethodNameResolver.class)
    public void testNumber(Number number) {
        // When:
        int count = counter.incrementAndGet();

        // Then:
        assertThat(count).isEqualTo(number.intValue());
    }

For a full code example, see DataProviderResolverAcceptanceTest.java. Please also note that this is not the best test case as it depends on the order of the found dataprovider methods. But it works as a good enough demonstration, producing the following test result:

<testcase name="testNumber[0: 1]" classname="com.tngtech.java.junit.dataprovider.ResolverTest" time="0.145"/>
<testcase name="testNumber[0: 2]" classname="com.tngtech.java.junit.dataprovider.ResolverTest" time="0.0"/>
<testcase name="testNumber[1: 3]" classname="com.tngtech.java.junit.dataprovider.ResolverTest" time="0.0"/>
<testcase name="testNumber[0: 4]" classname="com.tngtech.java.junit.dataprovider.ResolverTest" time="0.001"/>
<testcase name="testNumber[1: 5]" classname="com.tngtech.java.junit.dataprovider.ResolverTest" time="0.0"/>
<testcase name="testNumber[2: 6.0]" classname="com.tngtech.java.junit.dataprovider.ResolverTest" time="0.0"/>

Please note that currently the numbering is reset for every dataprovider method, i.e. it starts three times from zero with three dataproviders. If this causes problems for you, please do not hesitate to open an issue here.

Change @DataProvider location

Since: v1.5.0

This feature is especially helpful if you will reuse a @DataProvider for multiple tests of different classes.

@RunWith(DataProviderRunner.class)
public class DataProviderFromOtherLocationTest {

    @Test
    @UseDataProvider(value = "dataProviderIsStringLengthGreaterTwo",
                     location = StringDataProvider.class)
    public void testIsStringLengthGreaterThanTwo(String str, boolean expected) {
        // When:
        boolean isGreaterThanTwo = (str == null) ? false : str.length() > 2;

        // Then:
        assertThat(isGreaterThanTwo).isEqualTo(expected);
    }
}
import com.tngtech.java.junit.dataprovider.DataProvider;

public class StringDataProvider {

    @DataProvider
    public static Object[][] dataProviderIsStringLengthGreaterTwo() {
        // @formatter:off
        return new Object[][] {
                { "",       false },
                { "1",      false },
                { "12",     false },
                { "123",    true },
                { "Test",   true },
            };
        // @formatter:on
    }
}

Varargs support

Since: v1.10.0

    @DataProvider
    public static Object[][] dataProviderVarargs3() {
        // @formatter:off
        return $$(
                $('a'),
                $('b', 0),
                $('c', 1, 2, 3)
            );
        // @formatter:on
    }

    @Test
    @UseDataProvider("dataProviderVarargs3")
    public void testVarargs3(char c, int... is) {
        assertNotNull(c);
        for (int i : is) {
            assertNotNull(i);
        }
    }

    @Test
    // @formatter:off
    @DataProvider({
            "",
            "a",
            "x, y, z",
        })
    // @formatter:on
    public void testVarargs2(String... strings) {
        for (String s : strings) {
            assertNotNull(s);
        }
    }

@BeforeClass support (JUnit4 only)

Note: For JUnit5 dataproviders this works out-of-the-box as expected since v2.0 using @BeforeAll annotation. This only affects JUnit4 dataprovider implementation.

Since: v1.9.2 Until: v1.10.3

!!!Breaking change!!! if you use the results of a @BeforeClass method within a dataprovider method in v1.10.4 removing the @BeforeClass support again, as there were various complains, see #49 and #64.

All static @BeforeClass setup methods are executed before any @DataProvider such that all static fields can be prepared in these test setup methods and smoothly accessed within @DataProvider methods:

    private static String emptyString = null;
    private static String notEmptyString = null;

    @BeforeClass
    public static void setup() {
        emptyString = "";
        notEmptyString = "notEmpty";
    }

    @DataProvider
    public static Object[][] dataProviderNotNullStringsSetInBeforeClass() {
        // @formatter:off
        return new Object[][] {
                { emptyString },
                { notEmptyString },
        };
        // @formatter:on
    }

    @Test
    @UseDataProvider("dataProviderNotNullStringsSetInBeforeClass")
    public void testNotNullStringsSetInBeforeClass(String str) {
        // Given:

        // Expected:
        assertThat(str != null).isTrue();
    }

Note: @ClassRule are not executed before the static @DataProvider method currently because I see no possibility doing this. The problem is that JUnit rules are undividable "befores" and "afters" which internally surround a Statement executing all created tests (besides some other things). To create all tests, though, the dataprovider methods must be executed already ... :-(

Customize test method name

  • @DataProviders formatter() since: v2.3
  • @DataProviders format() since: v1.9.0

If you want to customize the test method names because they are too long or should fit a special need for later parsing, you can do this easily by customizing @DataProviders formatter(). By default DataProviderPlaceholderFormatter is used. This falls back on the origin mechansim via @DataProviders format(). The following placeholders are provided by default (also see com.tngtech.java.junit.dataprovider.internal.placeholder:

Placeholder Example Description
%c DataProviderRunnerTest Simple name of test method class (= Class#getSimpleName())
%cc com.tngtech.java .junit.dataprovider .DataProviderRunnerTest Canonical name of test method class (= Class#getCanonicalName())
%m testIsEmptyString Simple name of test method (= Method#getName())
%cm com.tngtech.test .java.junit.dataprovider .DataProviderJavaAcceptanceTest .testIsEmptyString(java.lang.String) Complete signature of test method (= Method#toString())
%i 13 Index of the dataprovider test of current test method (starting at 0}). Useful to generate unique method descriptions.
%a[x] test, , 4 Subscripting all arguments by positive or negative index (e.g. 1) and range (0..-2). All indices may either be positive (starting at 0 and increment) to number arguments from the beginning or negative (starting from -1 and decrement) to number arguments from the end.
%na[x] arg0=test, arg1=, arg2=4 Subscripting all named arguments as a[x] does but prefixes arguments with parameter names.
%p[x] test, , 4 Same as %a[x].

But be aware of using this feature, as it may break the following other features:

  • Running all tests for one dataprovider (in various IDEs) and jumping to a test method by double-clicking it (in Eclipse) does only work if the custom @DataProvider#format() starts with the test method name (= %m) followed by a character which is not a valid java identifier, e.g. " ", "[", "(", ":", or "-".
  • If test method names are not unique within its class, it can cause indeterministic behavior!
    // ...

    static class PlusTestNameFormatter implements DataProviderTestNameFormatter {
        @Override
        public String format(Method testMethod, int invocationIndex, List<Object> arguments) {
            return String.format("%s: %2d + %2d = %2d", testMethod.getName(), arguments.get(0), arguments.get(1),
                    arguments.get(2));
        }
    }

    @DataProvider(formatter = PlusTestNameFormatter.class)
    static Object[][] dataProviderPlus() {
        // @formatter:off
        return new Object[][] {
            {  0,  0,  0 },
            { -1,  0, -1 },
            {  0,  1,  1 },
            {  1,  1,  2 },
            {  1, -1,  0 },
            { -1, -1, -2 },
        };
        // @formatter:on
    }

    @Test
    @UseDataProvider
    void testPlus(int a, int b, int expected) {
        // Expect:
        assertThat(a + b).isEqualTo(expected);
    }

    @DataProvider(format = "%m: %p[0] * %p[1] == %p[2]")
    public static Object[][] dataProviderMultiply() {
        // @formatter:off
        return new Object[][] {
                {  0,  0,  0 },
                {  0,  1,  0 },
                {  1,  1,  1 },
                {  1, -1, -1 },
                { -1, -1,  1 },
                {  1,  2,  2 },
                {  6,  7, 42 },
            };
        // @formatter:on
    }

    @Test
    @UseDataProvider("dataProviderMultiply")
    public void testMultiply(int a, int b, int expected) {
        // Expect:
        assertThat(a * b).isEqualTo(expected);
    }

    // ...

The results would look like:

<testcase name="testMultiply: 0 * 0 == 0"   classname="com.tngtech.java.junit.dataprovider.TestNameFormatterTest" time="0.058"/>
<testcase name="testMultiply: -1 * 0 == 0"  classname="com.tngtech.java.junit.dataprovider.TestNameFormatterTest" time="0.0"/>
<testcase name="testMultiply: 0 * 1 == 0"   classname="com.tngtech.java.junit.dataprovider.TestNameFormatterTest" time="0.0"/>
<testcase name="testMultiply: 1 * 1 == 1"   classname="com.tngtech.java.junit.dataprovider.TestNameFormatterTest" time="0.0"/>
<testcase name="testMultiply: 1 * -1 == -1" classname="com.tngtech.java.junit.dataprovider.TestNameFormatterTest" time="0.001"/>
<testcase name="testMultiply: -1 * -1 == 1" classname="com.tngtech.java.junit.dataprovider.TestNameFormatterTest" time="0.0"/>
<testcase name="testMultiply: 1 * 2 == 2"   classname="com.tngtech.java.junit.dataprovider.TestNameFormatterTest" time="0.0"/>
<testcase name="testMultiply: -1 * 2 == -2" classname="com.tngtech.java.junit.dataprovider.TestNameFormatterTest" time="0.0"/>
<testcase name="testMultiply: -1 * -2 == 2" classname="com.tngtech.java.junit.dataprovider.TestNameFormatterTest" time="0.0"/>
<testcase name="testMultiply: -1 * -2 == 2" classname="com.tngtech.java.junit.dataprovider.TestNameFormatterTest" time="0.0"/>
<testcase name="testMultiply: 6 * 7 == 42"  classname="com.tngtech.java.junit.dataprovider.TestNameFormatterTest" time="0.001"/>

For more information about the default placeholders, see DataProvider.java. If you want to further customize the test method name, you can implement and add your own placeholder as following:

public class StripParameterLengthPlaceholder extends ParameterPlaceholder {
    private final int maxLength;

    public StripParameterLengthPlaceholder(int maxLength) {
        this.maxLength = maxLength;
    }

    @Override
    protected String formatAll(Object[] parameters) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < parameters.length; i++) {
            String formattedParameter = format(parameters[i]);
            if (formattedParameter.length() > maxLength) {
                stringBuilder.append(formattedParameter.substring(0, maxLength - 5));
                stringBuilder.append("...");
                stringBuilder.append(formattedParameter.substring(formattedParameter.length() - 5));
            } else {
                stringBuilder.append(formattedParameter);
            }
            if (i < parameters.length - 1) {
                stringBuilder.append(", ");
            }
        }
        return stringBuilder.toString();
    }
}
    public static class StripParameterLengthPlaceholderFormatter extends DataProviderPlaceholderFormatter {

        public StripParameterLengthPlaceholderFormatter(String nameFormat) {
            super(nameFormat, new StripParameterLengthPlaceholder(10));
        }
    }

    @DataProvider(formatter = StripParameterLengthPlaceholderFormatter.class)
    public static String[] dataProviderEqualsIgnoreCase() {
        // @formatter:off
        return new String[] {
                "veryVeryLongMethodNameWhichMustBeStripped,                                      null, false",
                "veryVeryLongMethodNameWhichMustBeStripped,                                          , false",
                "veryVeryLongMethodNameWhichMustBeStripped, veryVeryLongMethodNameWhichMustBeStripped,  true",
                "veryverylongmethodnamewhichmustbestripped, veryVeryLongMethodNameWhichMustBeStripped,  true",
                "veryVeryLongMethodNameWhichMustBeStripped, veryverylongmethodnamewhichmustbestripped,  true",
        };
        // @formatter:on
    }

    @Test
    @UseDataProvider
    public void testEqualsIgnoreCase(String methodName1, String methodName2, boolean expected) {
        // Expected:
        assertThat(methodName1.equalsIgnoreCase(methodName2)).isEqualTo(expected);
    }

For the full example, see CustomPlaceholderAcceptanceTest.java.

Access FrameworkMethod in @DataProvider

Since: v1.8.0

The cache option on @DataProvider was introduced with v2.2. Before that the default was equivalent to cache = true such that the reuse of dynamic dataproviders was not optimal.

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface ExternalFile {
        public enum Format {
            CSV,
            XML,
            XLS;
        }

        Format format();
        String value();
    }

    @DataProvider(cache = false)
    public static Object[][] loadFromExternalFile(FrameworkMethod testMethod) {
        String testDataFile = testMethod.getAnnotation(ExternalFile.class).value();
        // Load the data from the external file here ...
        return new Object[][] { { testDataFile } };
    }

    @Test
    @UseDataProvider("loadFromExternalFile")
    @ExternalFile(format = ExternalFile.Format.CSV, value = "testdata.csv")
    public void testThatUsesUniversalDataProvider(String testData) {
        // Expect:
        assertThat(testData).isEqualTo("testdata.csv");
    }

Thanks to @barancev for the pull request (#28) for parameters to dataproviders and to @weiro-9-w7 for addressing the caching problems (#105).

Utility methods

Since: v1.9.0

To be able to use the full power of these utility methods, add com.tngtech.java.junit.dataprovider.DataProviders.* as [Eclipse][] type favorite (via Preferences -> Java -> Editor -> Content Assist -> Favorites and New Type....

    import static com.tngtech.java.junit.dataprovider.DataProviders.*;

    @DataProvider
    public static Object[][] dataProviderAdd() {
        // @formatter:off
        return $$(
                $( -1, -1, -2 ),
                $( -1,  0, -1 ),
                $(  0, -1, -1 ),
                $(  0,  0,  0 ),
                $(  0,  1,  1 ),
                $(  1,  0,  1 ),
                $(  1,  1,  2 )
        );
        // @formatter:on
    }

    @DataProvider
    public static Object[][] dataProviderStringIsNullOrEmpty() {
        return testForEach(null, "");
    }

    @DataProvider
    public static Object[][] dataProviderRoundingMode() {
        return testForEach(java.math.RoundingMode.class);
    }

Inspired by @michaeladler.

Convention over configuration

Dataprovider method name

Since: v1.10.4

If @UseDataProvider does not provide an explicitly given value() the dataprovider method name is tried to be determined automatically by the following conventions in order:

  • @DataProvider annotated method which name equals the test method name
  • @DataProvider annotated method whereby prefix is replaced one out of the following table:
Prefix Replacement
test dataProvider
test data
  • @DataProvider annotated method whereby additional prefix dataProvider or data is given. Also the first letter of the original test method name is uppercased, e.g. shouldReturnTwoForOnePlusOne could correspond to dataProviderShouldReturnTwoForOnePlusOne.

Kotlin support

You can just add a @JvmStatic annotation to the companion object function containing the dataprovider data and it plays nicely :-)

Here is a complete example:

import com.tngtech.java.junit.dataprovider.DataProvider
import com.tngtech.java.junit.dataprovider.DataProviderRunner
import com.tngtech.java.junit.dataprovider.DataProviders.`$$`
import com.tngtech.java.junit.dataprovider.DataProviders.`$`
import com.tngtech.java.junit.dataprovider.UseDataProvider
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(DataProviderRunner::class)
class DataTest {

    companion object {
        @DataProvider
        @JvmStatic
        fun testRelativeAngleProvider(): Array<Array<Any>> = `$$`(
                // @formatter:off
            `$`( 0,  0,  0),

            `$`( 0,  1,  1),
            `$`( 1,  0,  1),
            `$`( 1,  1,  2),

            `$`( 0, -1, -1),
            `$`(-1,  0, -1),
            `$`(-1, -1, -2),

            `$`( 1, -1,  0),
            `$`(-1,  2,  1)
            // @formatter:on
        )
    }

    @Test
    @UseDataProvider(value = "testRelativeAngleProvider")
    fun testRelativeAngles(a: Int, b: Int, c: Int) {
        assert(a + b == c);
    }
}

OSGi compatible MANIFEST.MF

Since: v1.10.1