Kotlin で JUnit4 のテストケースを書く
-
JUnit4 / Parameterized
-
Mockito 利用
-
テスト対象が Lombok, MapStruct 使用
な状況で Kotlin でテストコードを書きます。
先にまとめを書いておきます。
-
(IntelliJ で Java のコードを
.kt
にペーストすると Kotlin コードに変換してくれるが、コレジャナイ感溢れるコードになったのであまり自動変換コードを信用するのは危険かも) -
@Mock
や@InjectMocks
を付与したフィールドはlateinit var
-
static
メソッドを書きたい場合はcompanion object
で囲って@JvmStatic
アノテーションを付与する-
今回の場合は JUnit4 のパラメータ供給メソッド(
@Parameters
)が該当
-
-
Kotlin では
when
は予約語なのでバッククオートで囲う必要がある -
Kotlin で Mockito の
any()
を利用すると "ArgumentMatchers.any(mViewModel.CountSubscriber::class.java) must not be null" みたいなエラーになる-
…らしいが今回のコードでは再現しなかった
-
対処法は、
any()
を定義し直したりmockito-kotlin
を利用したりする方法がある
-
Javaでテストを書くとこんな感じになります(テストの中身は適当です。今回の検証はテストを実行できるようにするのが目的なので):
package com.github.yukihane.example;
import lombok.RequiredArgsConstructor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(Parameterized.class)
@RequiredArgsConstructor
public class MyControllerJavaTest {
private final Param param;
@Mock
private MyService service;
@Mock
private MyParamMapper paramMapper;
@InjectMocks
private MyController sut;
public record Param(MyParamDTO input, MyParamDTO expected) {
}
@Parameterized.Parameters
public static List<Param> provide() {
return List.of(new Param(new MyParamDTO("alice", 16), new MyParamDTO("alice", 17)),
new Param(new MyParamDTO("bob", 32), new MyParamDTO("bob", 33)));
}
@Before
public void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
public void normal() {
MyService service = new MyServiceImpl();
MyController controller = new MyController(service, MyParamMapper.INSTANCE);
MyParamDTO res = controller.index(param.input);
assertThat(res).isEqualTo(param.expected);
}
@Test
public void mocking() {
when(paramMapper.convert(any(MyParamDTO.class))).thenReturn(mock(MyParam.class));
when(service.execute(any())).thenReturn((mock(MyParam.class)));
MyParamDTO exp = mock(MyParamDTO.class);
when(paramMapper.convert(any(MyParam.class))).thenReturn(exp);
MyParamDTO res = sut.index(param.input);
assertThat(res).isSameAs(exp);
}
}
これを Kotlin で書くと次のようになります:
package com.github.yukihane.example
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.mockito.ArgumentMatchers.any
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
@RunWith(Parameterized::class)
class MyControllerKtTest(private val param: Param) {
@Mock
private lateinit var service: MyService
@Mock
private lateinit var outputMapper: MyParamMapper
@InjectMocks
private lateinit var sut: MyController
data class Param(val input: MyParamDTO, val expected: MyParamDTO)
companion object {
@JvmStatic
@Parameterized.Parameters
fun provide(): List<Param> {
return listOf(
Param(
MyParamDTO("alice", 16),
MyParamDTO("alice", 17)
),
Param(
MyParamDTO("bob", 32),
MyParamDTO("bob", 33)
)
)
}
}
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
}
@Test
fun normal() {
val service: MyService = MyServiceImpl()
val controller = MyController(service, MyParamMapper.INSTANCE)
val res = controller.index(param.input)
assertThat(res).isEqualTo(param.expected)
}
@Test
fun mocking() {
`when`(outputMapper.convert(any(MyParamDTO::class.java))).thenReturn(mock(MyParam::class.java))
`when`(service.execute(any())).thenReturn(mock(MyParam::class.java))
val exp = mock(MyParamDTO::class.java)
`when`(outputMapper.convert(any(MyParam::class.java))).thenReturn(exp)
val res = sut.index(param.input)
assertThat(res).isSameAs(exp)
}
}