Unit Test: Mockito Annotations with Kotlin
Our team converted all unit tests from Java to Kotlin. During these practices, we learned more and became familiar with Kotlin and Mockito.
Use lateinit val for @Mock, @Captor and other annotations to avoid !! or ?
In Java, Mockito provides annotation processors to generate inject mocked spied or other instances at compile time. Here is normal example,
public class Test {
@Captor
ArgumentCaptor<Foo> captor; @Spy
Foo spyOnFoo = new Foo("argument"); @Spy
Bar spyOnBar; @InjectMocks
ArticleManager manager; @Before
public void init() {
MockitoAnnotations.initMocks(this);
} ....
}
And when converting to Kotlin, these injected properties become read-only but Nullable type, as following
class Test {
@Captor
val captor: ArgumentCaptor<Foo>?; @Spy
val spyOnFoo = Foo("argument"); @Spy
val spyOnBar: Bar?; @InjectMocks
val manager: ArticleManager?; @Before
fun init() {
MockitoAnnotations.initMocks(this);
} ....
}
This is not good… I need to use ? or !! operations when using captor, spyOnBar and manager because they are Nullable types. This is redundant because Mockito will initialize these properties after compiled.
To reduce these unnecessary ? or !!, we can adjust these properties lateinit var and change them Non-Null type
class Test {
@Captor
lateinit var captor: ArgumentCaptor<Foo>; @Spy
val spyOnFoo = Foo("argument"); @Spy
lateinit var spyOnBar: Bar; @InjectMocks
lateinit var manager: ArticleManager; @Before
fun init() {
MockitoAnnotations.initMocks(this);
}....
}
By this simple modification, we can ignore these Null Safety check and all unit tests work correctly either.
Use @JvmField for @Rule to make those properties public in Java
Sometimes we need to use PowerMockito to mock static methods or other power opeations. And we need to set PowerMockRule public with @Rule.
@Rule
public PowerMockRule rule = new PowerMockRule();And when we use plugin to convert, it will become
@Rule
val rule = PowerMockRule()Then, even the property was set to be public, you will get compile error, ValidationError: The @Rule 'rule' must be public. This is because Kotlin will convert this variable into private field with public getter, so it will become
@Rule
@NotNull
private final PowerMockRule rule = new PowerMockRule();
@NotNull
public final PowerMockRule getRule() {
return this.rule;
}That’s why it conflicts JUnit Test Rule’s requirement, Rules must be public. To fix this, we just need @JvmField to tell Kotlin compiler not to generate getters/setters for this property and just expose it as a field.
@Rule
@JvmField
val rule = PowerMockRule()