Bird
Raised Fist0
JUnittesting~15 mins

Custom argument providers in JUnit - Build an Automation Script

Choose your learning style10 modes available
Automate a parameterized test using a custom argument provider in JUnit
Preconditions (2)
Step 1: Create a custom argument provider class implementing ArgumentsProvider
Step 2: Override provideArguments method to supply test data
Step 3: Create a parameterized test method in the test class
Step 4: Annotate the test method with @ParameterizedTest and @ArgumentsSource with the custom provider
Step 5: Run the test and verify it executes once per data set
✅ Expected Result: The parameterized test runs successfully for each data set provided by the custom argument provider, passing all assertions.
Automation Requirements - JUnit 5
Assertions Needed:
Verify the test method runs for each argument set
Assert the input values are as expected inside the test
Best Practices:
Use @ArgumentsSource annotation with a custom ArgumentsProvider
Implement provideArguments method returning Stream<Arguments>
Keep test data inside the provider class for reusability
Use descriptive test method names
Avoid hardcoding data inside the test method
Automated Solution
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 CustomArgumentProviderTest {

    static class MyCustomArgumentProvider implements ArgumentsProvider {
        @Override
        public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
            return Stream.of(
                Arguments.of("apple", 5),
                Arguments.of("banana", 6),
                Arguments.of("cherry", 6)
            );
        }
    }

    @ParameterizedTest(name = "Test with fruit={0} and length={1}")
    @ArgumentsSource(MyCustomArgumentProvider.class)
    void testWithCustomArguments(String fruit, int length) {
        assertTrue(fruit.length() == length, "Fruit length should match the provided length");
    }
}

This test class defines a MyCustomArgumentProvider that implements ArgumentsProvider. It overrides provideArguments to return a stream of Arguments objects, each containing a fruit name and its length.

The test method testWithCustomArguments is annotated with @ParameterizedTest and @ArgumentsSource to use the custom provider. It receives the parameters and asserts the fruit's length matches the expected length.

This setup keeps test data separate from the test logic, making tests cleaner and reusable.

Common Mistakes - 4 Pitfalls
{'mistake': 'Not implementing ArgumentsProvider interface correctly', 'why_bad': "The test framework won't recognize the provider and tests won't run with parameters.", 'correct_approach': 'Implement ArgumentsProvider and override provideArguments method returning Stream<Arguments>.'}
Hardcoding test data inside the test method instead of the provider
Using incorrect annotation like @ValueSource instead of @ArgumentsSource for custom providers
Returning null or empty stream from provideArguments
Bonus Challenge

Now add data-driven testing with 3 different sets of fruit names and lengths using the custom argument provider.

Show Hint

Practice

(1/5)
1. What is the main purpose of a custom argument provider in JUnit?
easy
A. To generate test reports
B. To create new test methods automatically
C. To supply test data to parameterized tests in a custom way
D. To replace the @Test annotation

Solution

  1. Step 1: Understand the role of argument providers

    Argument providers supply data to parameterized tests, allowing tests to run multiple times with different inputs.
  2. Step 2: Identify the purpose of custom argument providers

    Custom argument providers let you define your own way to supply test data beyond built-in sources.
  3. Final Answer:

    To supply test data to parameterized tests in a custom way -> Option C
  4. Quick Check:

    Custom argument providers = supply test data [OK]
Hint: Custom providers supply test data uniquely for parameterized tests [OK]
Common Mistakes:
  • Confusing argument providers with test method creators
  • Thinking they replace @Test annotation
  • Assuming they generate reports
2. Which interface must you implement to create a custom argument provider in JUnit?
easy
A. ArgumentsSource
B. ArgumentsProvider
C. TestTemplateInvocationContextProvider
D. TestInstancePostProcessor

Solution

  1. Step 1: Recall JUnit interfaces for argument providers

    The interface to implement for custom argument providers is ArgumentsProvider.
  2. Step 2: Differentiate from other interfaces

    ArgumentsSource is an annotation, not an interface. The others serve different purposes.
  3. Final Answer:

    ArgumentsProvider -> Option B
  4. Quick Check:

    Implement ArgumentsProvider for custom data [OK]
Hint: Implement ArgumentsProvider interface for custom data supply [OK]
Common Mistakes:
  • Confusing ArgumentsSource annotation with interface
  • Choosing unrelated interfaces
  • Not overriding provideArguments method
3. Given this custom argument provider code snippet, what will be the output when the parameterized test runs?
public class MyProvider implements ArgumentsProvider {
    @Override
    public Stream<Arguments> provideArguments(ExtensionContext context) {
        return Stream.of(Arguments.of("apple"), Arguments.of("banana"));
    }
}

@ParameterizedTest
@ArgumentsSource(MyProvider.class)
void testFruits(String fruit) {
    System.out.println(fruit);
}
medium
A. apple\nbanana (each on separate lines)
B. apple banana
C. Compilation error due to missing imports
D. Runtime error due to wrong method signature

Solution

  1. Step 1: Analyze the provideArguments method

    The method returns a stream with two Arguments: "apple" and "banana".
  2. Step 2: Understand parameterized test execution

    The test runs twice, once with "apple" and once with "banana", printing each on its own line.
  3. Final Answer:

    apple\nbanana (each on separate lines) -> Option A
  4. Quick Check:

    Stream of Arguments runs test twice [OK]
Hint: Each argument runs test once, prints on separate lines [OK]
Common Mistakes:
  • Assuming output is on one line
  • Thinking code has syntax errors without imports
  • Confusing method signature correctness
4. Identify the error in this custom argument provider implementation:
public class BadProvider implements ArgumentsProvider {
    public Stream<Arguments> provideArguments() {
        return Stream.of(Arguments.of(1), Arguments.of(2));
    }
}
medium
A. Method provideArguments must have ExtensionContext parameter
B. Missing @Override annotation
C. Arguments.of() cannot take integers
D. Class must extend ArgumentsSource

Solution

  1. Step 1: Check method signature requirements

    The provideArguments method must override the interface method and accept ExtensionContext as a parameter.
  2. Step 2: Identify the missing parameter

    The code's provideArguments method lacks the ExtensionContext parameter, causing a signature mismatch.
  3. Final Answer:

    Method provideArguments must have ExtensionContext parameter -> Option A
  4. Quick Check:

    Correct method signature requires ExtensionContext [OK]
Hint: provideArguments must accept ExtensionContext parameter [OK]
Common Mistakes:
  • Ignoring method signature parameters
  • Thinking @Override is mandatory but not fixing signature
  • Confusing Arguments.of() usage
5. You want to create a custom argument provider that supplies pairs of integers where the second is always double the first. Which implementation correctly achieves this?
public class DoubleProvider implements ArgumentsProvider {
    @Override
    public Stream<Arguments> provideArguments(ExtensionContext context) {
        // Fill in the blank
    }
}
hard
A. return Stream.of(Arguments.of(1, 3), Arguments.of(2, 5), Arguments.of(3, 7));
B. return Stream.of(Arguments.of(1, 1), Arguments.of(2, 2), Arguments.of(3, 3));
C. return Stream.empty();
D. return Stream.of(Arguments.of(1, 2), Arguments.of(2, 4), Arguments.of(3, 6));

Solution

  1. Step 1: Understand the requirement

    Each pair must have the second integer double the first.
  2. Step 2: Evaluate each option

    return Stream.of(Arguments.of(1, 2), Arguments.of(2, 4), Arguments.of(3, 6)); correctly pairs (1,2), (2,4), (3,6). Other options provide pairs like (1,3),(2,5),(3,7) or equal numbers (1,1),(2,2),(3,3), or an empty stream.
  3. Final Answer:

    return Stream.of(Arguments.of(1, 2), Arguments.of(2, 4), Arguments.of(3, 6)); -> Option D
  4. Quick Check:

    Second integer = 2 * first integer [OK]
Hint: Check if second number is double the first in pairs [OK]
Common Mistakes:
  • Providing pairs where second is not double
  • Returning empty stream by mistake
  • Confusing argument order
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