SpringMockKを使ってみた 

kotlinのmockライブラリで有名なMockK。便利ですよね。
MockitoのようにSpringに対応してくれればもっと便利なのになあと思っていたら、[SpringMockK](https://github.com/Ninja-Squad/springmockk)というそのものずばりなライブラリがあったので試してみました。
# 準備
[Spring Initializr](https://start.spring.io/)で作ったひな型に以下のようなサービスを追加します。
テストではこのサービスをmockにします。

```kotlin:HogeService.kt
package com.example.demo.service

import org.springframework.stereotype.Service

@Service
class HogeService {

    fun hoge() = "HogeService.hoge()"

    fun fuga() = "HogeService.fuga()"
}
```

また、HogeServiceを利用するコンポーネントも作成します。

```kotlin:Main.kt
package com.example.demo

import com.example.demo.service.HogeService
import org.springframework.stereotype.Component

@Component
class Main(
    private val hogeService: HogeService
) {

    fun main() = "${hogeService.hoge()}, ${hogeService.fuga()}"
}
```

build.gradleのdependenciesに以下を追加します。
`com.ninja-squad:springmockk`だけでなく`net.bytebuddy:byte-buddy`も追加しないと動きませんでした。

```gradle:build.gradle
dependencies {
    // ...略...
    // SpringMockK
	testImplementation 'net.bytebuddy:byte-buddy:1.9.12'
	testImplementation 'com.ninja-squad:springmockk:1.1.2'
    // ...略...
}
```


# MockkBean
以下のようにmock化したいサービスに`@MockkBean`アノテーションを付与するとよしなにmock化してくれます。
ちゃんとverifyもできますね。

```kotlin:SpringMockKTest.kt
package com.example.demo.service

import com.example.demo.Main
import com.ninjasquad.springmockk.MockkBean
import io.mockk.every
import io.mockk.verify
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit.jupiter.SpringExtension

@ExtendWith(SpringExtension::class)
@SpringBootTest
class SpringMockKTest {

    @MockkBean
    private lateinit var hogeService: HogeService

    @Autowired
    private lateinit var main: Main

    @Test
    fun mainTest() {
        every { hogeService.hoge() } returns "SpringMockKHoge.hoge()"
        every { hogeService.fuga() } returns "SpringMockKHoge.fuga()"

        assertEquals("SpringMockKHoge.hoge(), SpringMockKHoge.fuga()", main.main())

        verify {
            hogeService.hoge()
            hogeService.fuga()
        }
    }
}
```

# SpykBean
`@SpykBean`アノテーションでspyもできます。

```kotlin:SpringMockKSpyTest.kt
package com.example.demo.service

import com.example.demo.Main
import com.ninjasquad.springmockk.MockkBean
import com.ninjasquad.springmockk.SpykBean
import io.mockk.every
import io.mockk.verify
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit.jupiter.SpringExtension

@ExtendWith(SpringExtension::class)
@SpringBootTest
class SpringMockKSpyTest {

    @SpykBean
    private lateinit var hogeService: HogeService

    @Autowired
    private lateinit var main: Main

    @Test
    fun mainTest() {
        every { hogeService.hoge() } returns "SpringMockKHoge.hoge()"

        assertEquals("SpringMockKHoge.hoge(), HogeService.fuga()", main.main())

        verify {
            hogeService.hoge()
            hogeService.fuga()
        }
    }
}
```

# おまけ
SpringMockKを利用せずにSpringでMockKを利用したい場合、以下の方法があるようです。
ただし、うまく動くのはバージョンが2.0.9.RELEASEまでで、Spring Initializrで生成したバージョン2.1.4.RELEASEではエラーで動きませんでした。

```kotlin:MockKTest.kt
package com.example.demo.service

import com.example.demo.Main
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.test.context.junit.jupiter.SpringExtension

// Springのバージョンが2.0.9.RELEASE までなら動く
@ExtendWith(SpringExtension::class)
@SpringBootTest
class MockKTest {

    @TestConfiguration
    class MockKConfig {

        @Bean
        fun hogeService() = mockk<HogeService>()
    }

    @Autowired
    private lateinit var hogeService: HogeService

    @Autowired
    private lateinit var main: Main

    @Test
    fun mainTest() {
        every { hogeService.hoge() } returns "MockKHoge.hoge()"
        every { hogeService.fuga() } returns "MockKHoge.fuga()"

        assertEquals("MockKHoge.hoge(), MockKHoge.fuga()", main.main())

        verify {
            hogeService.hoge()
            hogeService.fuga()
        }
    }
}
```

# 参考
- https://github.com/Ninja-Squad/springmockk
- https://start.spring.io/
- https://medium.com/@darych90/kotlin-spring-boot-mockk-6d1c1a6463ac