I have a project in Kotlin.
I created a @ConfigurationProperties
class and I would like to know the best practices for unit tests.
The following is my properties class:
@ConstructorBinding
@ConfigurationProperties(prefix = "myapp")
data class MyAppProperties(
/**
* Base path to be used by myapp. Default is '/search'.
*/
val basePath: String = "/myapp"
)
I inject MyAppProperties in my Controller:
@RestController
final class MyAppController(
myAppProperties: MyAppProperties
) {
...
}
I want to test my Controller:
@ExtendWith(MockitoExtension::class)
internal class MyAppControllerTest {
@Mock
lateinit var myAppProperties: MyAppProperties
@InjectMocks
lateinit var myAppController: MyAppController
...
}
But I have the following Mockito error:
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class com.myapp.MyAppProperties
Mockito cannot mock/spy because :
- final class
What is the best solution to fix this problem:
- Allow Mockito to mock final class: https://blog.mindorks.com/mockito-cannot-mock-in-kotlin
/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: mock-maker-inline
This solution seems good since we don't modify the existing code but it adds a behavior to Mockito for the whole project and all final classes.
- Make my properties class
open
open class MyAppProperties...
This solution requires modifying the code and makes the class extensible, which may not be a good thing?
- Make all @ConfigurationProperties classes open via the Maven configuration:
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
<arg>-Xjvm-default=enable</arg>
</args>
<compilerPlugins>
<plugin>all-open</plugin>
</compilerPlugins>
<pluginOptions>
<option>all-open:annotation=org.springframework.boot.context.properties.ConfigurationProperties</option>
</pluginOptions>
</configuration>
...
</plugin>
This solution does not require modifying the code but make all @ConfigurationProperties classes open
, which may not be a good thing?
- Do not mock the properties class and initialize it in MyAppControllerTest:
@ExtendWith(MockitoExtension::class)
internal class MyAppControllerTest {
val myAppProperties: MyAppProperties = MyAppProperties("/mypath")
...
}
This does not allow us to give a specific behaviour according to the test.