Kotlin で JUnit4 のテストケースを書く
   2 min read
  • 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" みたいなエラーになる

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)
    }
}