JUnit Jupiter Parameterized Test Extension
This project provides a powerful extension for JUnit Jupiter to enhance parameterized testing capabilities. It offers a flexible and type-safe way to generate test data for various parameter types, including numbers, dates, times, string, and enums.
The JUnit Jupiter Parameterized Test Extension simplifies the process of creating comprehensive test suites by automating the generation of test data. This allows developers to focus on writing test logic while the extension handles the complexities of data provision.
Usage Instructions
Installation
To use this extension in your JUnit Jupiter tests, add the following dependency to your project:
dependencies {
testImplementation("com.wesleyhome.test:junit-jupiter-params-generated:<latestVersion>")
}
For Maven projects, add the following to your pom.xml:
<dependency>
<groupId>com.wesleyhome.test</groupId>
<artifactId>junit-jupiter-params-generated</artifactId>
<version>${<latestVersion>}</version>
<scope>provided</scope>
</dependency>
Ensure you have JUnit Jupiter 5.11.4 or later in your project dependencies.
Getting Started
-
Import the necessary annotations and classes:
import com.wesleyhome.test.jupiter.annotations.GeneratedParametersTest
import com.wesleyhome.test.jupiter.annotations.number.IntRangeSource
-
Create a test method using the
@GeneratedParametersTestannotation:
@GeneratedParametersTest
fun testWithGeneratedParameters(@IntRangeSource(min = 1, max = 10) value: Int) {
assertTrue(value in 1..10)
}
-
Run your tests as usual with JUnit Jupiter.
Configuration Options
The extension provides various annotations for different parameter types:
-
@IntRangeSource,@LongRangeSource,@DoubleRangeSource,@FloatRangeSource: For numeric ranges -
@InstantRangeSource,@LocalDateRangeSource,@LocalDateTimeRangeSource,@LocalTimeRangeSource: For date and time ranges -
@StringSource: For string values -
@InstantSource,@LocalDateSource,@LocalDateTimeSource,@LocalTimeSource: For specific date and time values -
@RandomInstantSource: For generating random Instant values -
@IntSource,@LongSource,@DoubleSource,@FloatSource: For specific numeric values
Each annotation has specific configuration options. Refer to the documentation for detailed information on each annotation.
Common Use Cases
-
Testing with a range of integers:
@GeneratedParametersTest
fun testIntRange(@IntRangeSource(min = 1, max = 100, step = 10) value: Int) {
assertTrue(value in 1..100 step 10)
}
@GeneratedParametersTest
void testIntRange(@IntRangeSource(min = 1, max = 100, step = 10) int value) {
assertTrue(value >= 1 && value <= 100 && (value - 1) % 10 == 0);
}
-
Testing with date ranges:
@GeneratedParametersTest
fun testDateRange(@LocalDateRangeSource(min = "2023-01-01", max = "2023-12-31") date: LocalDate) {
assertTrue(date.year == 2023)
}
@GeneratedParametersTest
void testDateRange(@LocalDateRangeSource(min = "2023-01-01", max = "2023-12-31") LocalDate date) {
assertEquals(2023, date.getYear());
}
-
Testing with enum values:
@GeneratedParametersTest
fun testEnumValues(value: TestEnum) {
assertNotNull(value)
}
@GeneratedParametersTest
void testEnumValues(TestEnum value) {
assertNotNull(value);
}
Annotation Processor
The JUnit Jupiter Parameterized Test Extension includes an annotation processor that can be used for compile-time validation of your test parameters. This helps catch potential issues early in the development process, improving the overall quality and reliability of your tests.
Setting up the Annotation Processor
To use the annotation processor, add the following dependency to your project:
annotationProcessor("com.wesleyhome:junit-jupiter-params-generated-processor:<latestVersion>")
For Maven projects, add the following to your pom.xml:
<dependency>
<groupId>com.wesleyhome</groupId>
<artifactId>junit-jupiter-params-generated-processor</artifactId>
<version>${<latestVersion>}</version>
<scope>provided</scope>
</dependency>
Using the Annotation Processor
The annotation processor automatically validates the usage of parameter annotations in your test classes. Here's an example of how it works with Java:
import com.wesleyhome.test.jupiter.annotations.GeneratedParametersTest;
import com.wesleyhome.test.jupiter.annotations.number.IntRangeSource;
import org.junit.jupiter.api.Assertions;
public class AnnotationProcessorExampleTest {
@GeneratedParametersTest
void testWithValidRange(@IntRangeSource(min = 1, max = 10) int value) {
Assertions.assertTrue(value >= 1 && value <= 10);
}
@GeneratedParametersTest
void testWithInvalidRange(@IntRangeSource(min = 10, max = 1) int value) {
// This will cause a compile-time error
Assertions.fail("This test should not compile");
}
}
In the example above, the second test method will cause a compile-time error because the min value is greater than the max value in the @IntRangeSource annotation. The annotation processor detects this issue and reports it during compilation, preventing invalid test configurations from making it into your test suite.
Benefits of Using the Annotation Processor
-
Early detection of configuration errors
-
Improved test reliability
-
Faster feedback loop during development
-
Reduced runtime errors related to parameter generation
By leveraging the annotation processor, you can ensure that your parameterized tests are correctly configured before they are even executed, saving time and improving the overall quality of your test suite.
Data Flow
The JUnit Jupiter Parameterized Test Extension processes test methods in the following way:
-
The
@GeneratedParametersTestannotation is detected by JUnit Jupiter. -
The extension scans the test method parameters for supported annotations.
-
For each annotated parameter, the corresponding data provider is invoked to generate test data.
-
The generated data is used to create test invocations, each with a unique set of parameter values.
-
JUnit Jupiter executes the test method for each set of generated parameter values.
[Test Method] -> [Parameter Scan] -> [Data Generation] -> [Test Invocations] -> [Test Execution]
This flow allows for efficient and flexible parameterized testing, with the extension handling the complexities of data generation and test invocation.
Creating Custom Annotations and Data Providers
The JUnit Jupiter Parameterized Test Extension is designed to be extensible, allowing developers to create custom annotations and data providers for specific testing needs. This section explains how to create your own annotations and corresponding data providers.
Creating a Custom Annotation
To create a custom annotation:
-
Define a new annotation class in your project.
-
Annotate your custom annotation with
@Target(AnnotationTarget.VALUE_PARAMETER)to specify that it can be used on method parameters. -
Add any necessary attributes to your annotation.
Example:
@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@SourceProvider(CustomSourceDataProvider::class)
@MustBeDocumented
annotation class CustomSource(val min: Int, val max: Int, val step: Int) : Annotation
Creating a Custom Data Provider
To create a custom data provider:
-
Create a new class that implements the
ParameterDataProvider<T>interface, whereTis the type of data your provider will generate. -
Implement the
providesDataFormethod to determine if your provider can handle a givenTestParameter. -
Implement the
createParameterOptionsDatamethod to generate the test data based on the annotation attributes.
Example:
class CustomSourceDataProvider : ParameterDataProvider<Int> {
override fun createParameterOptionsData(testParameter: TestParameter): List<Int> {
val annotation = testParameter.annotations.filterIsInstance<CustomSource>().first()
return (annotation.min..annotation.max step annotation.step).toList()
}
}
Integrating Custom Annotations and Data Providers
To integrate your custom annotation and data provider:
-
Use your custom annotation in your test methods:
@GeneratedParametersTest
fun testWithCustomSource(@CustomSource(min = 1, max = 10, step = 2) value: Int) {
assertTrue(value in listOf(1, 3, 5, 7, 9))
}
By following these steps, you can extend the JUnit Jupiter Parameterized Test Extension with your own custom annotations and data providers, tailoring it to your specific testing requirements.
Testing & Quality
To run the tests for this project:
./gradlew test
Troubleshooting
-
Issue: Tests are not picking up the generated parameters
-
Ensure that you have added the correct dependency to your project.
-
Verify that you are using the
@GeneratedParametersTestannotation on your test methods. -
Check that your parameter annotations are correctly configured.
-
Issue: Compilation errors with annotation processor
-
Make sure you have the annotation processor correctly set up in your build configuration.
-
Clean and rebuild your project to ensure the annotation processor runs.
For more detailed troubleshooting, enable debug logging by adding the following to your logback-test.xml or log4j2-test.xml:
<logger name="com.wesleyhome.test.jupiter" level="DEBUG"/>