iT邦幫忙

2023 iThome 鐵人賽

DAY 21
0
Kotlin

Kotlin魔法:Spring Boot 3的fp奇幻冒險系列 第 21

[城鎮] Spring Boot 與 TestContainer 的互動

  • 分享至 

  • xImage
  •  

前情提要

前面介紹了環境變數如何設置,以及TestContainer是什麼,今天我們要將TestContainer給啟動起來,讓我們的測試實際去打真正的MongoDB!

事情準備 Pom

<dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>mongodb</artifactId>
            <version>1.19.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>testcontainers</artifactId>
            <version>1.19.1</version>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>1.19.1</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.8.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-test -->
        <dependency>
            <groupId>org.jetbrains.kotlinx</groupId>
            <artifactId>kotlinx-coroutines-test</artifactId>
            <version>1.7.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>6.0.12</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>3.1.4</version>
            <scope>test</scope>
        </dependency>

主要是安裝一些Testconatainer的套件,測試套件,這樣我們才能使用它XD

實際的Code

package Service

import Repo.CustomerRepo
import model.Age
import model.Customer
import model.CustomerVO
import model.Email
import model.Name
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.mockito.InjectMocks
import org.mockito.Mock
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.testcontainers.containers.MongoDBContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers
import org.testcontainers.utility.DockerImageName
import service.CustomerService
import kotlin.test.Test

@Testcontainers
@SpringBootTest(classes = [CustomerTest::class])
class CustomerTest {
    companion object {
        @DynamicPropertySource
        @JvmStatic
        fun setDynamicProperties(registry: DynamicPropertyRegistry) {
            registry.add("mongodb.url") {
                mongoContainer.replicaSetUrl
            }
        }

        @Container
        val mongoContainer =
            MongoDBContainer(DockerImageName.parse("mongo:latest"))
                .withExposedPorts(27017)
    }

    @BeforeEach
    fun setUp() {
        mongoContainer.start()
    }

    @AfterEach
    fun tearDown() {
        mongoContainer.stop()
    }

    @Mock
    private lateinit var customerRepo: CustomerRepo

    @InjectMocks
    private lateinit var customerService: CustomerService

    @Test
    fun testCustomerVoToCustomer() {
        val customerVO = CustomerVO(
            name = "John",
            contactInfo = "hello@xx.com",
            age = 18,
        )

        val result = customerService.customerVOToCustomer(customerVO)

        val expected = Customer(
            name = Name("John"),
            contactInfo = Email("hello@xx.com"),
            age = Age(18),
        )

        Assertions.assertEquals(expected, result)
    }
}



我們在Test class上面加上這兩個Annotation,讓我們可以用TestContainer,以及Spring boot test的一些好方法
@Testcontainers
@SpringBootTest(classes = [CustomerTest::class])

TestContainer生命週期

我們在Companion object裡面設置好Testcontainer,並且讓它置換application properties的值(mongodb.url),讓他可以正確的讀到mongo,另外在每個測試前可以先讓container跑起來,結束後讓它結束,這樣就不會影響到各別的TestCase,也可以清空mongodb就好,就不用重啟。

companion object {
        @DynamicPropertySource
        @JvmStatic
        fun setDynamicProperties(registry: DynamicPropertyRegistry) {
            registry.add("mongodb.url") {
                mongoContainer.replicaSetUrl
            }
        }

        @Container
        val mongoContainer =
            MongoDBContainer(DockerImageName.parse("mongo:latest"))
                .withExposedPorts(27017)
    }

    @BeforeEach
    fun setUp() {
        mongoContainer.start()
    }

    @AfterEach
    fun tearDown() {
        mongoContainer.stop()
    }

Mock

因為我們要測試CustomerService的function,所以要將Repo給Mock,然後注入到Service裡面,因為我們的Service會用到它。這樣就可以將我們的測試使用TestContainer來跑MongoDB囉。

    @Mock
    private lateinit var customerRepo: CustomerRepo

    @InjectMocks
    private lateinit var customerService: CustomerService

總結

TestContainer跑完會將Container清掉,這樣可以達到完整的測試,接著就可以玩出很多的變化,如果需要別人的服務時,就起一個TestConainer來測試!像是Redis、Mariadb,都可以用,很方便XD

有了這些工具,我們就可以對我們的測試有比較高的信心水準,對我們的程式就可以比較信任,信心水準upup,製造我們完美的Weekend!


上一篇
[小草原] Spring Boot的application.properties 讀取大解密
下一篇
[城鎮] Redis - 超快的速度! Speed!
系列文
Kotlin魔法:Spring Boot 3的fp奇幻冒險30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言