The interesting thing about Enums in Kotlin, is that it's too easy add context to them.
Even if Enums in Kotlin has a los of possibilities, we'll use just the basic and necessary for our needs.
Although Enums in Kotlin have many possibilities, we will use what is just and necessary for our needs.
To see it, we'll use the MowerOrientation Value object.
As always, we set up a test scenario to start working.
import mower.mower.exception.InvalidOrientationException
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import kotlin.test.Test
private const val ORIENTATION = "N"
private const val INVALID_ORIENTATION = "H"
internal class MowerOrientationTest {
@Test
fun `Should be build`() {
val orientation = MowerOrientation.build(ORIENTATION)
assertInstanceOf(MowerOrientation::class.java, orientation)
assertThat(orientation.value).isEqualTo(ORIENTATION)
}
@ParameterizedTest(name = "{index} => orientation = ''{0}''")
@ValueSource(strings = [ "N", "S", "E", "W" ])
fun `Should be build with valid orientations`(value: String) {
val orientation = MowerOrientation.build(value)
assertThat(orientation.value).isEqualTo(value)
}
@Test
fun `Should throw exception for invalid Orientation`() {
assertThrows(InvalidOrientationException::class.java) {
MowerOrientation.build(INVALID_ORIENTATION)
}
}
}
We want to validate that the value we pass to the value object satisfies all four possible directions. We also want a custom error and the code remains elegant (without try-catch)
Notice how the test is configured with several possible values.
The ParameterizedTest in JUnit is the equivalent of the PHPUnit DataProvider. We can add context to it by launching them with a template like the one described in the test.
...
@ParameterizedTest(name = "{index} => orientation = ''{0}''")
...
The required object can be articulated as follows.
import mower.mower.exception.InvalidOrientationException
@JvmInline
value class MowerOrientation private constructor(val value: String) {
private enum class Compass(val value: String){
NORTH("N"),
EAST("E"),
SOUTH("S"),
WEST("W")
}
init {
if(null === enumValues<Compass>().find { it.value == value}){
throw InvalidOrientationException.withValues(value, Orientations.values().map { it.value }.toString())
}
}
@JvmStatic
companion object {
fun build(value: String): MowerOrientation
{
return MowerOrientation(value)
}
}
}
The most interesting thing is that we can create a constant, which serves as a context, and an associated value, which is what the system will use to initialize the value of the value object. There are more ways to feed the parameters in the documentation that I put below.
Ongoing dilemma...
Kotlin is more in favor of throwing exceptions than using the null value as a response. We can do some changes to meet your requirements. We would save ourselves the cost of processing with "find" and "map" in case of responding with an error.
Although I prefer the first implementation, I understand that if we don't handle null values, we can have a hidden problem in our code, something that doesn't happen in an unhandled exception.
...
private enum class Compass{ N, E, S, W }
init {
try {
Compass.valueOf(value)
} catch (exception: IllegalArgumentException) {
throw InvalidOrientationException.withValues(value, Compass.values().contentToString())
}
}
...
If later we want to use enum to retrieve information, in the first way we must use "find" and treat the null value, which is not optimal. For me is the eternal dilemma... and there's no hard answer. Depends... like everything...
The worst thing I've seen is an escalation of try-catch between objects that calling other objects, and that usually ends badly... hard to read and manage.
In this case I think that it's more accurate to go the Kotlin way and we'll leave you with the exception handling. Since later we'll have to use the Enum values for other things and we would be doing "find" every time, thus hampering the utility funcionality.
Links and resources
Enum class
https://kotlinlang.org/docs/enum-classes.html
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/enum-values.html
Parameterized test sources
https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests-sources
https://www.petrikainulainen.net/programming/testing/junit-5-tutorial-writing-parameterized-tests/
Top comments (0)