Bird
Raised Fist0
JUnittesting~7 mins

@ArgumentsSource with custom providers in JUnit

Choose your learning style10 modes available
Introduction

We use @ArgumentsSource to supply test methods with custom sets of data. This helps us run the same test many times with different inputs, making sure our code works well in all cases.

When you want to test a method with many different inputs that are not simple constants.
When your test data needs to be generated dynamically or comes from a complex source.
When you want to keep your test code clean by separating data generation from test logic.
When built-in sources like <code>@ValueSource</code> or <code>@CsvSource</code> are not enough.
When you want to reuse the same data provider for multiple test methods.
Syntax
JUnit
@ArgumentsSource(MyArgumentsProvider.class)
void testMethod(Type param) {
    // test code
}

class MyArgumentsProvider implements ArgumentsProvider {
    @Override
    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
        // return Stream of Arguments
    }
}

The @ArgumentsSource annotation takes a class that implements ArgumentsProvider.

The provideArguments method returns a Stream of Arguments objects, each representing one set of parameters.

Examples
This example runs the test twice, once with "apple" and once with "banana" as input.
JUnit
@ArgumentsSource(MyArgsProvider.class)
void testWithCustomData(String input) {
    assertNotNull(input);
}

class MyArgsProvider implements ArgumentsProvider {
    @Override
    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
        return Stream.of(Arguments.of("apple"), Arguments.of("banana"));
    }
}
This example tests numbers 1, 2, and 3 to check if they are greater than zero.
JUnit
@ArgumentsSource(NumberProvider.class)
void testNumbers(int number) {
    assertTrue(number > 0);
}

class NumberProvider implements ArgumentsProvider {
    @Override
    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
        return IntStream.rangeClosed(1, 3).mapToObj(Arguments::of);
    }
}
Sample Program

This test runs four times with even numbers 2, 4, 6, and 8. Each time it checks if the number is even.

JUnit
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;

import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertTrue;

public class CustomArgumentsSourceTest {

    @ParameterizedTest
    @ArgumentsSource(EvenNumberProvider.class)
    void testEvenNumbers(int number) {
        assertTrue(number % 2 == 0, "Number should be even");
    }

    static class EvenNumberProvider implements ArgumentsProvider {
        @Override
        public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
            return Stream.of(2, 4, 6, 8).map(Arguments::of);
        }
    }
}
OutputSuccess
Important Notes

Make sure your ArgumentsProvider class is static or top-level and public if used outside the test class.

Each Arguments object can hold multiple parameters if your test method has more than one argument.

Use Stream.of or other stream sources to generate test data flexibly.

Summary

@ArgumentsSource lets you provide custom test data for parameterized tests.

You create a class implementing ArgumentsProvider to supply data.

This helps test many cases cleanly and flexibly.

Practice

(1/5)
1. What is the main purpose of using @ArgumentsSource in JUnit tests?
easy
A. To mark a test method as disabled
B. To provide custom test data from a class implementing ArgumentsProvider
C. To specify the order of test execution
D. To inject mock objects into tests

Solution

  1. Step 1: Understand the role of @ArgumentsSource

    This annotation is used to supply custom arguments to parameterized tests by referencing a class that implements ArgumentsProvider.
  2. Step 2: Differentiate from other annotations

    Other options like disabling tests or ordering are handled by different annotations, not @ArgumentsSource.
  3. Final Answer:

    To provide custom test data from a class implementing ArgumentsProvider -> Option B
  4. Quick Check:

    @ArgumentsSource supplies custom test data [OK]
Hint: Remember: @ArgumentsSource links to custom data providers [OK]
Common Mistakes:
  • Confusing @ArgumentsSource with @Disabled
  • Thinking it controls test order
  • Assuming it injects mocks
2. Which of the following is the correct way to declare a custom ArgumentsProvider class for use with @ArgumentsSource?
easy
A. public class MyProvider implements ArgumentsProvider { public Stream<Arguments> provideArguments(ExtensionContext context) { return Stream.of(Arguments.of(1)); } }
B. public class MyProvider { public Stream<Arguments> provideArguments() { return Stream.of(Arguments.of(1)); } }
C. public class MyProvider implements ArgumentsSource { public Stream<Arguments> provideArguments() { return Stream.of(Arguments.of(1)); } }
D. public class MyProvider implements ArgumentsProvider { public List<Arguments> provideArguments() { return List.of(Arguments.of(1)); } }

Solution

  1. Step 1: Check interface implementation and method signature

    The class must implement ArgumentsProvider and override provideArguments(ExtensionContext) returning a Stream<Arguments>.
  2. Step 2: Validate return type and method parameters

    public class MyProvider implements ArgumentsProvider { public Stream<Arguments> provideArguments(ExtensionContext context) { return Stream.of(Arguments.of(1)); } } correctly implements the interface and method signature. public class MyProvider { public Stream<Arguments> provideArguments() { return Stream.of(Arguments.of(1)); } } misses the interface and method parameter. public class MyProvider implements ArgumentsSource { public Stream<Arguments> provideArguments() { return Stream.of(Arguments.of(1)); } } wrongly implements ArgumentsSource which is an annotation, not an interface. public class MyProvider implements ArgumentsProvider { public List<Arguments> provideArguments() { return List.of(Arguments.of(1)); } } returns a List instead of a Stream.
  3. Final Answer:

    public class MyProvider implements ArgumentsProvider { public Stream<Arguments> provideArguments(ExtensionContext context) { return Stream.of(Arguments.of(1)); } } -> Option A
  4. Quick Check:

    Implement ArgumentsProvider with correct method signature [OK]
Hint: Implement ArgumentsProvider with provideArguments(ExtensionContext) [OK]
Common Mistakes:
  • Forgetting to implement ArgumentsProvider
  • Wrong method signature or missing parameter
  • Returning List instead of Stream
  • Confusing ArgumentsSource annotation with interface
3. Given the following custom provider and test method, what will be the output when the test runs?
public class NumberProvider implements ArgumentsProvider {
    public Stream<Arguments> provideArguments(ExtensionContext context) {
        return Stream.of(Arguments.of(2), Arguments.of(4));
    }
}

@ParameterizedTest
@ArgumentsSource(NumberProvider.class)
void testIsEven(int number) {
    assertTrue(number % 2 == 0);
}
medium
A. Compilation error due to missing annotation
B. Test fails because 4 is not even
C. Both tests pass because 2 and 4 are even numbers
D. Test fails because 2 is not even

Solution

  1. Step 1: Analyze the provided arguments

    The provider returns two arguments: 2 and 4, both even numbers.
  2. Step 2: Check the assertion logic

    The test asserts that the number modulo 2 equals 0, which is true for both 2 and 4.
  3. Final Answer:

    Both tests pass because 2 and 4 are even numbers -> Option C
  4. Quick Check:

    2 % 2 == 0 and 4 % 2 == 0 [OK]
Hint: Check if all provided numbers satisfy the assertion condition [OK]
Common Mistakes:
  • Assuming 4 is odd
  • Ignoring the assertion logic
  • Confusing test failure with compilation error
4. Identify the error in the following custom ArgumentsProvider implementation:
public class StringProvider implements ArgumentsProvider {
    public List<Arguments> provideArguments(ExtensionContext context) {
        return List.of(Arguments.of("test"));
    }
}
medium
A. Method name should be getArguments instead of provideArguments
B. Class must implement ArgumentsSource instead of ArgumentsProvider
C. No error, code is correct
D. Method should return Stream<Arguments>, not List<Arguments>

Solution

  1. Step 1: Check the return type of provideArguments

    The ArgumentsProvider interface requires provideArguments to return a Stream<Arguments>, but the code returns a List<Arguments>.
  2. Step 2: Validate interface and method name

    The class correctly implements ArgumentsProvider and the method name is correct.
  3. Final Answer:

    Method should return Stream<Arguments>, not List<Arguments> -> Option D
  4. Quick Check:

    Return type must be Stream<Arguments> [OK]
Hint: Remember: provideArguments returns Stream, not List [OK]
Common Mistakes:
  • Returning List instead of Stream
  • Confusing interface with annotation
  • Wrong method name
5. You want to test a method that accepts a pair of integers and returns their sum. You decide to use @ArgumentsSource with a custom provider that supplies pairs. Which of the following ArgumentsProvider implementations correctly provides pairs of integers for the test?
hard
A. public class PairProvider implements ArgumentsProvider { public Stream<Arguments> provideArguments(ExtensionContext context) { return Stream.of(Arguments.of(1, 2), Arguments.of(3, 4)); } }
B. public class PairProvider implements ArgumentsProvider { public Stream<Arguments> provideArguments(ExtensionContext context) { return Stream.of(Arguments.of(new int[]{1, 2}), Arguments.of(new int[]{3, 4})); } }
C. public class PairProvider implements ArgumentsProvider { public Stream<Arguments> provideArguments(ExtensionContext context) { return Stream.of(Arguments.of(List.of(1, 2)), Arguments.of(List.of(3, 4))); } }
D. public class PairProvider implements ArgumentsProvider { public Stream<Arguments> provideArguments(ExtensionContext context) { return Stream.of(Arguments.of(1), Arguments.of(2), Arguments.of(3), Arguments.of(4)); } }

Solution

  1. Step 1: Understand the test method parameters

    The test method expects two integer parameters per test invocation, so each Arguments must supply two integers.
  2. Step 2: Evaluate each provider's argument format

    public class PairProvider implements ArgumentsProvider { public Stream<Arguments> provideArguments(ExtensionContext context) { return Stream.of(Arguments.of(1, 2), Arguments.of(3, 4)); } } correctly provides pairs of integers as separate arguments. public class PairProvider implements ArgumentsProvider { public Stream<Arguments> provideArguments(ExtensionContext context) { return Stream.of(Arguments.of(new int[]{1, 2}), Arguments.of(new int[]{3, 4})); } } provides arrays which may not match the method signature expecting two int parameters. public class PairProvider implements ArgumentsProvider { public Stream<Arguments> provideArguments(ExtensionContext context) { return Stream.of(Arguments.of(List.of(1, 2)), Arguments.of(List.of(3, 4))); } } provides lists which also do not match two int parameters directly. public class PairProvider implements ArgumentsProvider { public Stream<Arguments> provideArguments(ExtensionContext context) { return Stream.of(Arguments.of(1), Arguments.of(2), Arguments.of(3), Arguments.of(4)); } } provides single integers, not pairs.
  3. Final Answer:

    public class PairProvider implements ArgumentsProvider { public Stream<Arguments> provideArguments(ExtensionContext context) { return Stream.of(Arguments.of(1, 2), Arguments.of(3, 4)); } } -> Option A
  4. Quick Check:

    Each Arguments.of() must match method parameters [OK]
Hint: Match Arguments.of() parameters count to test method parameters [OK]
Common Mistakes:
  • Passing arrays or lists instead of separate parameters
  • Providing single arguments when pairs are needed
  • Not matching argument count to method parameters
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard