Argument aggregation helps combine multiple inputs into one place for easier testing. It makes tests cleaner and easier to read.
Argument aggregation in JUnit
@ArgumentsAggregator public class MyAggregator implements ArgumentsAggregator { @Override public Object aggregateArguments(ArgumentsAccessor accessor, ParameterContext context) { // Combine arguments from accessor and return one object } }
The @ArgumentsAggregator annotation marks a class that combines arguments.
The aggregateArguments method receives all arguments and returns a single combined object.
@ArgumentsAggregator public class PersonAggregator implements ArgumentsAggregator { @Override public Person aggregateArguments(ArgumentsAccessor accessor, ParameterContext context) { String name = accessor.getString(0); int age = accessor.getInteger(1); return new Person(name, age); } }
@ParameterizedTest
@CsvSource({"John,30", "Jane,25"})
void testPerson(@AggregateWith(PersonAggregator.class) Person person) {
assertNotNull(person);
}This test uses argument aggregation to convert CSV inputs into Person objects. It checks that the person is not null and age is over 20.
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.aggregator.ArgumentsAccessor; import org.junit.jupiter.params.aggregator.ArgumentsAggregator; import org.junit.jupiter.params.aggregator.AggregateWith; import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.*; class Person { String name; int age; Person(String name, int age) { this.name = name; this.age = age; } } @ArgumentsAggregator class PersonAggregator implements ArgumentsAggregator { @Override public Person aggregateArguments(ArgumentsAccessor accessor, org.junit.jupiter.params.aggregator.ParameterContext context) { return new Person(accessor.getString(0), accessor.getInteger(1)); } } public class PersonTest { @ParameterizedTest @CsvSource({"Alice,30", "Bob,25"}) void testPerson(@AggregateWith(PersonAggregator.class) Person person) { assertNotNull(person); assertTrue(person.age > 20); } }
Argument aggregation works only with parameterized tests in JUnit 5.
Use @AggregateWith on the test method parameter to apply your aggregator.
Aggregators help keep test methods clean when many inputs are involved.
Argument aggregation groups multiple inputs into one object for easier testing.
Use @ArgumentsAggregator and implement ArgumentsAggregator interface.
Apply aggregators with @AggregateWith in parameterized tests.
Practice
@ArgumentsAggregator in JUnit parameterized tests?Solution
Step 1: Understand the role of @ArgumentsAggregator
This annotation is used to create a custom aggregator that combines multiple input arguments into one object.Step 2: Recognize the benefit in parameterized tests
Aggregating arguments simplifies test method signatures and improves readability by passing one object instead of many parameters.Final Answer:
To combine multiple method arguments into a single object for easier testing -> Option AQuick Check:
@ArgumentsAggregator = combine inputs [OK]
- Confusing @ArgumentsAggregator with parallel test execution
- Thinking it generates test data automatically
- Assuming it skips tests
Solution
Step 1: Recall the correct annotation for applying aggregators
The annotation to apply a custom aggregator on a parameter is @AggregateWith.Step 2: Match the syntax
The correct syntax is @AggregateWith(CustomAggregator.class) followed by the parameter type and name.Final Answer:
@AggregateWith(CustomAggregator.class) MyObject obj -> Option AQuick Check:
@AggregateWith = apply aggregator [OK]
- Using @ArgumentsSource instead of @AggregateWith
- Misspelling the annotation name
- Confusing @AggregateWith with @ArgumentAggregator
public class PersonAggregator implements ArgumentsAggregator {
@Override
public Person aggregateArguments(ArgumentsAccessor accessor, ParameterContext context) {
return new Person(accessor.getString(0), accessor.getInteger(1));
}
}
@ParameterizedTest
@CsvSource({"Alice,30", "Bob,25"})
void testPerson(@AggregateWith(PersonAggregator.class) Person person) {
assertTrue(person.getAge() > 20);
}Solution
Step 1: Understand the aggregator behavior
The aggregator creates a Person object with name and age from CSV input.Step 2: Evaluate the assertion for each input
Both Alice (30) and Bob (25) have ages greater than 20, so assertTrue(person.getAge() > 20) passes for both.Final Answer:
Test passes for both Alice and Bob -> Option CQuick Check:
Age > 20 for both inputs = pass [OK]
- Assuming 25 is less than or equal to 20
- Confusing assertion logic
- Ignoring aggregator mapping
public class ProductAggregator implements ArgumentsAggregator {
@Override
public Product aggregateArguments(ArgumentsAccessor accessor, ParameterContext context) {
String name = accessor.getString(0);
int price = accessor.getInteger(1);
return new Product(name, price);
}
}
@ParameterizedTest
@CsvSource({"Laptop,1000", "Phone,abc"})
void testProduct(@AggregateWith(ProductAggregator.class) Product product) {
assertNotNull(product);
}Solution
Step 1: Analyze CSV input and aggregator code
The second CSV line has 'abc' where an integer price is expected.Step 2: Understand the impact on aggregation
Calling accessor.getInteger(1) on 'abc' will throw a runtime exception (NumberFormatException).Final Answer:
The second CSV input 'Phone,abc' causes a runtime error due to invalid integer -> Option DQuick Check:
Invalid integer input = runtime error [OK]
- Ignoring invalid CSV data causing exceptions
- Assuming aggregator method signature is wrong
- Thinking annotation is missing
Person object with fields name and age. You have CSV data with columns name, age, and city. How can you write a custom aggregator to ignore the city column and only aggregate name and age into Person?Solution
Step 1: Understand the CSV data and Person constructor
CSV has three columns but Person only needs name and age.Step 2: Implement aggregator to pick only needed columns
Use ArgumentsAccessor.getString(0) and getInteger(1) to get name and age, ignoring the third city column.Final Answer:
Use ArgumentsAccessor to get only the first two columns and create Person ignoring city -> Option BQuick Check:
Pick needed columns only in aggregator [OK]
- Trying to pass extra columns to Person constructor
- Changing CSV source instead of aggregator logic
- Not implementing ArgumentsAggregator interface
Ready to prove it?
Pass the test and this pattern moves unknown
