본문 바로가기
Java & Kotlin/Spring

Testcontainers MongoDB init script 실행하기

by devson 2021. 10. 29.

통합 테스트를 하다보면 가끔씩 인프라에 대해 init script(초기화 스크립트)를 작성하고 실행해야할 때가 있다.

이번 포스팅에서는 Spring Application에서 통합 테스트 시 테스트 인프라를 설정하기 위해 자주 사용되는 Testcontainers와 DB Module 중 하나인 MongoDB Module을 사용했을 때 초기 데이터를 위해 init script를 어떻게 실행할지에 대해 알아보도록 하겠다.

 

(예제는 여기에서 확인할 수 있다.)


프로젝트 생성

Spring Boot 프로젝트를 아래와 같이 Spring Data MongoDB, Testcontainers 의존성을 추가하고 생성한다.

 

예제 도메인 클래스 추가

프로젝트 생성 후 MongoDB에 저장할 도메인 클래스를 추가하자

import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.mapping.Document
import org.springframework.data.mongodb.core.mapping.Field
import org.springframework.data.mongodb.core.mapping.FieldType

@Document(collection = "members")
data class Member(
    @Field(name = "name")
    val name: String,

    @Field(name = "address")
    val address: String,
) {
    @Id
    @Field(name = "_id", targetType = FieldType.STRING)
    var id: String? = null
}

id는 스크립트로 편하게 저장할 수 있도록 단순 String으로 지정하였다.

 

import org.springframework.data.mongodb.repository.MongoRepository

interface MemberRepository : MongoRepository<Member, String>

 

통합 테스트를 위한 Testcontainers 설정

JUnit을 사용하여 테스트를 할 때 Extension을 사용하면 플러그인 형식의 편리하고 깔끔한 테스트 코드를 짤 수 있다.

Testcontainers 설정은 이 Extension을 사용하여 아래와 같이 설정하도록한다.

import org.junit.jupiter.api.extension.BeforeAllCallback
import org.junit.jupiter.api.extension.Extension
import org.junit.jupiter.api.extension.ExtensionContext
import org.testcontainers.containers.MongoDBContainer
import org.testcontainers.utility.MountableFile

class MongodbTestcontainersExtension : Extension, BeforeAllCallback {
    companion object {
        private val mongodbContainer: MongoDBContainer = MongoDBContainer("mongo:4.4")
    }

    override fun beforeAll(context: ExtensionContext) {
        if (mongodbContainer.isRunning) {
            return
        }

        mongodbContainer.start()
        System.setProperty("spring.data.mongodb.uri", mongodbContainer.replicaSetUrl)
    }
}

 

init script 작성

init script는 test resources 디렉토리에 추가하도록 하겠다.

 

내용은 단순하게 DB에 document data를 하나 추가하는 것이다.

db.members.insertOne({_id: 'chris', name: 'Chris', address: 'Seoul'});

 

init script 실행 코드 작성

위에서 init script를 작성했기 때문에 이제 이를 테스트 실행 시 실행하도록 하자.

위에서 작성한 MongodbTestcontainersExtension에 실행코드를 추가한다.

 

import org.junit.jupiter.api.extension.BeforeAllCallback
import org.junit.jupiter.api.extension.Extension
import org.junit.jupiter.api.extension.ExtensionContext
import org.testcontainers.containers.MongoDBContainer
import org.testcontainers.utility.MountableFile

class MongodbTestcontainersExtension : Extension, BeforeAllCallback {
    companion object {
        private val mongodbContainer: MongoDBContainer = MongoDBContainer("mongo:4.4")
    }

    override fun beforeAll(context: ExtensionContext) {
        if (mongodbContainer.isRunning) {
            return
        }

        mongodbContainer.start()
        System.setProperty("spring.data.mongodb.uri", mongodbContainer.replicaSetUrl)

        // execute init script
        mongodbContainer.copyFileToContainer(MountableFile.forClasspathResource("init.js"), "/script/init.js")
        mongodbContainer.execInContainer("mongo", "/script/init.js")

        // it doesn't work...
//        mongodbContainer.copyFileToContainer(MountableFile.forClasspathResource("init.js"), "/docker-entrypoint-initdb.d/init.js")
    }
}

 

코드는 매우 단순하다.

1. resource 디렉토리에 있는 init.js 파일을 MongoDB container 내의 /script/init.js로 복사한다.

2. MongoDB container에 mongo /script/init.js 커맨드를 실행한다.

원하는 대로 코드가 실행되었다면 members collection에 데이터가 하나 있을 것이다.

 

(stackoverflow의 한 답변을 봤을 때 docker-entrypoint-initdb.d에 스크립트를 옮기면 될 것 같았으나 동작을 하지 않았다 🤔)

 

init script 실행 확인 테스트 작성

이제 init script가 실행됨을 확인하기 위한 테스트 코드를 작성하자.

 

먼저 앞서 만든 MongodbTestcontainersExtension을 사용해서 통합 테스트를 위한 기본 테스트 클래스를 생성하도록 하자.

import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest
@ExtendWith(MongodbTestcontainersExtension::class)
abstract class IntegrationTest {
}

 

그리고 이를 사용하여 데이터가 저장되었는지 확인을 하는 코드를 작성하자.

import com.devs0n.mongoinit.member.MemberRepository
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired

class TestcontainersMongoInitDataApplicationTests : IntegrationTest() {

    @Test
    fun contextLoads() {
    }

    @Autowired
    lateinit var memberRepository: MemberRepository

    @Test
    fun `test init script`() {
        val initMember = memberRepository.findById("chris")
        assertThat(initMember).isNotNull

        val member = initMember.get()
        assertThat(member.id).isEqualTo("chris")
        assertThat(member.name).isEqualTo("Chris")
        assertThat(member.address).isEqualTo("Seoul")
    }
}

(사실 테스트가 언제 실행될지 알 수 없으니 이 테스트는 단지 확인을 하기 위한 테스트라고 보면 될 것 같다)

 

자 이제 모든 준비는 끝났다.

테스트를 실행해보면 다음과 같이 테스트가 통과되는 것을 확인할 수 있고 init script가 잘 실행됨을 확인할 수 있다.

 

댓글