Test based on Enum type(DynamicTest)
Tags: java, kotlin
Published: 2024-04-07 05:47:41

When developing e-commerce, one encounters numerous Enums. The following Enum represents a simple product type code. There are two types: simple options and complex options.
```java
public enum ProductType {
	SIMPLE_OPTION,
	COMPLEX_OPTION
}
```

To test the parts that vary according to the product type, we can use `@ParameterizedTest` and `@EnumSource`.
```java
@ParameterizedTest
@EnumSource(names = {"SIMPLE_OPTION", "COMPLEX_OPTION"})
void enumSourceTest(ProductType productType) {
	System.out.println(productType);
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g44dqkbvphs191y77srl.png)

However, it's very common for business logic to branch based on the relationships between Enums, and one may want to test those parts.

The code below represents the relationship between the product type at the product level and the item type at the item level. While there are eight possible combinations of ProductType and ItemType, only four of these combinations are actually feasible.
```java
@Value
@Builder
public class Product {
	ProductType productType;

	List<Item> items;

	@Value
	@Builder
	public static class Item {
		ItemType itemType;
	}
}

// The relationship between ProductType and ItemType
// SIMPLE_OPTION - SIMPLE
// COMPLEX_OPTION - COMBINATION1
// COMPLEX_OPTION - COMBINATION2
// COMPLEX_OPTION - COMBINATION3
public enum ProductType {
	SIMPLE_OPTION,
	COMPLEX_OPTION
}

public enum ItemType {
	SIMPLE,
	COMBINATION1,
	COMBINATION2,
	COMBINATION3,
}
```

Therefore, we need to set up these four combinations separately, which can be done using `@ParameterizedTest` and `@MethodSource`.
```java
@ParameterizedTest
@MethodSource("productTypeAndItemTypeSet")
void methodSourceTest(ProductType productType, ItemType itemType) {
	System.out.println(productType + " : " + itemType);
}

private static Stream productTypeAndItemTypeSet() {
	return Stream.of(
		Arguments.of(
			ProductType.SIMPLE_OPTION,
			ItemType.SIMPLE
		),
		Arguments.of(
			ProductType.COMPLEX_OPTION,
			ItemType.COMBINATION1
		),
		Arguments.of(
			ProductType.COMPLEX_OPTION,
			ItemType.COMBINATION2
		),
		Arguments.of(
			ProductType.COMPLEX_OPTION,
			ItemType.COMBINATION3
		)
	);
}
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6jxpk87qjhcx722qkwpn.png)

There's also a way to utilize `org.junit.jupiter.api.DynamicTest` and `@TestFactory`.

Let's look at the code first. It sets up the four Enum combinations with `Stream.of` and then runs each through `DynamicTest.dynamicTest` within a flatMap structure.
```java
@TestFactory // It's not @Test but @TestFactory
Stream<DynamicTest> dynamicTestTest() {
	return Stream.of(
			Map.of(ProductType.SIMPLE_OPTION, ItemType.SIMPLE),
			Map.of(ProductType.COMPLEX_OPTION, ItemType.COMBINATION1),
			Map.of(ProductType.COMPLEX_OPTION, ItemType.COMBINATION2),
			Map.of(ProductType.COMPLEX_OPTION, ItemType.COMBINATION3)
		)
		.flatMap(testCaseMap ->
			testCaseMap.entrySet().stream()
				.map(it ->
					DynamicTest.dynamicTest(testCaseMap.toString(), () -> {
						ProductType productType = it.getKey();
						ItemType itemType = it.getValue();

						System.out.println(productType + " : " + itemType);
						}
					)
				)
		);
	}
}
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/acali7f6kdvqapysz9jq.png)

Java doesn't provide a Pair class by default, so using Map made the implementation a bit complicated, requiring the use of flatMap.

Creating it with Kotlin and using Pair would indeed make it simpler.
```kotlin
@TestFactory
fun dynamicTestTest(): Stream<DynamicTest> {
    return Stream.of(
        Pair(ProductType.SIMPLE_OPTION, ItemType.SIMPLE),
        Pair(ProductType.COMPLEX_OPTION, ItemType.COMBINATION1),
        Pair(ProductType.COMPLEX_OPTION, ItemType.COMBINATION2),
        Pair(ProductType.COMPLEX_OPTION, ItemType.COMBINATION3),
    )
        .map { pair ->
            DynamicTest.dynamicTest(pair.toString()) {
                println("${pair.first} : ${pair.second}")
            }
        }
}
```

cf) While Java doesn't provide a Pair class by default, we can use `org.springframework.data.util.Pair` provided by Spring.