A Guide to @ClassTemplate in JUnit 5
These articles are AI-generated summaries. Please check the original sources for full details.
1. Introduction
JUnit 5’s @ClassTemplate annotation allows a single test class to be executed multiple times, each with a different configuration, simplifying testing across various environments. This feature addresses the need for versatile testing, particularly when dealing with environment-dependent behavior.
Why This Matters
Traditional testing often requires duplicating test classes or embedding complex conditional logic to accommodate different environments. This approach leads to maintenance overhead and potential inconsistencies. The @ClassTemplate annotation provides a cleaner solution by separating test logic from environmental configuration, reducing code duplication and improving test reliability, especially in scenarios involving localization or feature flagging where environment-specific behavior is critical.
Key Insights
- InvocationContextProviders: Introduced in JUnit 5 for managing test execution contexts.
- Provider-Driven Execution: JUnit relies on providers to define how class templates are executed, promoting separation of concerns.
- Locale Extension Example: Demonstrates a practical application of
@ClassTemplatefor testing environment-specific behavior, such as date formatting across different locales.
Working Example
class DateFormatter {
public String format(LocalDate date) {
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)
.withLocale(Locale.getDefault());
return date.format(formatter);
}
}
class LocaleExtension implements BeforeEachCallback, AfterEachCallback {
private final Locale locale;
private Locale previous;
@Override
public void beforeEach(ExtensionContext context) {
previous = Locale.getDefault();
Locale.setDefault(locale);
}
@Override
public void afterEach(ExtensionContext context) {
Locale.setDefault(previous);
}
}
class DateLocaleClassTemplateProvider implements ClassTemplateInvocationContextProvider {
@Override
public Stream<ClassTemplateInvocationContext> provideClassTemplateInvocationContexts(ExtensionContext context) {
return Stream.of(Locale.US, Locale.GERMANY, Locale.ITALY, Locale.JAPAN)
.map(this::invocationContext);
}
private ClassTemplateInvocationContext invocationContext(Locale locale) {
return new ClassTemplateInvocationContext() {
@Override
public String getDisplayName(int invocationIndex) {
return "Locale: " + locale.getDisplayName();
}
@Override
public List<Extension> getAdditionalExtensions() {
return List.of(new LocaleExtension(locale));
}
};
}
}
private final DateFormatter formatter = new DateFormatter();
@Test
void givenDefaultLocale_whenFormattingDate_thenMatchesLocalizedOutput() {
LocalDate date = LocalDate.of(2025, 9, 30);
DateTimeFormatter expectedFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)
.withLocale(Locale.getDefault());
String expected = date.format(expectedFormatter);
String formatted = formatter.format(date);
LOG.info("Locale: {}, Expected: {}, Formatted: {}", Locale.getDefault(), expected, formatted);
assertEquals(expected, formatted);
}
Practical Applications
- Localization Testing: Verifying application behavior across different locales, as demonstrated in the example.
- Feature Flag Testing: Running tests with different feature flags enabled or disabled to ensure proper functionality under various configurations.
References:
Continue reading
Next article
Building a Modular Starter Kit for M5StickC-Plus2: From Messy Code to Clean Architecture
Related Content
Conditionally Ignore Tests in TestNG
Explore various approaches to ignore a test in TestNG conditionally, improving test suite flexibility and execution time.
How to Print JUnit Assertion Results
Discover three approaches to printing JUnit assertion results into logs for debugging, CI/CD monitoring, and detailed test reports.
A Guide to Engine Test Kit in Junit 5
JUnit 5's Engine Test Kit enables precise test execution tracking with detailed statistics.