backend ๊ฐ๋ฐ์ ํ๋ฉฐ test ํ๊ฒฝ์ ์ค์ ํ๋ ๊ฒ์ ํ์์ ์ธ ๊ณผ์ ์ด๋ค.
docker๊ฐ ๋์ค๊ณ ๋์๋ถํฐ๋ ํธํ๊ฒ ์ค์ ํ๊ธฐ ์ํด docker-compose๋ฅผ ํ์ฉํ๊ฑฐ๋ testcontainers๋ฅผ ์ฃผ๋ก ํ์ฉํ๋๋ฐ,
๊ฐ์ธ ๋
ธํธ๋ถ์ m1์ผ๋ก ๋ฐ๊พธ๊ณ ๋์ ์ฒ์์ผ๋ก testcontainers๋ฅผ mysql๋ก ์ค์ ํ๋ฉฐ ์๊ธด ์ด์๋ฅผ ์ ๋ฆฌํด๋ณด์๋ค.
์๊ฐ์ด ์์ผ์ ๋ถ๋ค์ ์ํด ๋ฐ๋ก ์ฑ๊ณตํ ์ฝ๋๋ถํฐ..
๋ณดํต์ testContainers๋ฅผ ์ฌ์ฉํ ๋์ junit container ๋ฐฉ์์ผ๋ก ๊ตฌํํ๋๋ฐ
๋๋ ๊ฐ์ธ์ ์ผ๋ก test์ abstract ํด๋์ค๋ ๋๋ฌด ๋ง์ ์ด๋
ธํ
์ด์
์ ๋ฃ๋ ๊ฒ์ ์ ํธํ์ง ์๊ธฐ ๋๋ฌธ์ ๋ณ๋๋ก lifecycle์ ๊ด๋ฆฌํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํ์๋ค.
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.stereotype.Component
import org.testcontainers.containers.MySQLContainer
import org.testcontainers.utility.DockerImageName
import javax.annotation.PreDestroy
@Component
@Order(value = Ordered.HIGHEST_PRECEDENCE)
class MySqlTestContainer {
@PreDestroy
fun stop() {
MY_SQL_CONTAINER.stop()
}
companion object {
@JvmStatic
val MY_SQL_CONTAINER: MySQLContainer<*> =
// image for linux/arm64/v8 m1 support
DockerImageName.parse("mysql/mysql-server:8.0.26")
.asCompatibleSubstituteFor("mysql")
.let { compatibleImageName -> MySQLContainer<Nothing>(compatibleImageName) }
.apply {
withDatabaseName(DATABASE_NAME)
withUsername(USERNAME)
withPassword(PASSWORD)
withEnv("MYSQL_USER", USERNAME)
withEnv("MYSQL_PASSWORD", PASSWORD)
withEnv("MYSQL_ROOT_PASSWORD", PASSWORD)
start()
}
const val DATABASE_NAME: String = "batch_test"
const val USERNAME: String = "root"
const val PASSWORD: String = "password"
}
}
์๋๋ ์์ ์ปจํ ์ด๋๋ฅผ dataSource๋ก ์ฐ๊ฒฐํ๋ ๋ถ๋ถ์ด๋ค.
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.DependsOn
import org.testcontainers.containers.MySQLContainer
import javax.sql.DataSource
@Configuration
class TestDataSourceConfiguration {
@Bean
@DependsOn("mySqlTestContainer")
fun dataSource(): DataSource =
DataSourceBuilder.create()
.url("jdbc:mysql://localhost:" +
"${MySqlTestContainer.MY_SQL_CONTAINER.getMappedPort(MySQLContainer.MYSQL_PORT)}/" +
MySqlTestContainer.DATABASE_NAME)
.driverClassName("com.mysql.cj.jdbc.Driver")
.username(MySqlTestContainer.USERNAME)
.password(MySqlTestContainer.PASSWORD)
.build()
}
no matching manifest for linux/arm64/v8 in the manifest list entries
๋จผ์ MySQLContainer<Nothing>("mysql:8.0.26")
๋ฒ์ ์ผ๋ก ์๋ํ๋ ์์ ๊ฐ์ ์๋ฌ๋ฅผ ๋ง๋ฅ๋จ๋ ธ๋ค.
์ฐพ์๋ณด๋ mysql dockerhub์ linux/amd64๋ฒ์ ๋ง ์ฌ๋ผ์์๊ณ m1์ ํด๋นํ๋ linux/arm64/v8์ผ๋ก ๋น๋๋ ๋ฒ์ ์ด ์์๋ค.
๋ฐ๋ผ์ ์ฐพ์๋ณด๋ mysql-server dockerhub์๋ m1์ ํด๋นํ๋ ๋ฒ์ ๋ ์ฌ๋ผ์์์ด์ ํด๋น ๋ฒ์ ์ผ๋ก ๋ณ๊ฒฝํ์๋ค.
java.lang.IllegalStateException: Failed to verify that image 'mysql/mysql-server:8.0.26' is a compatible substitute for 'mysql'. This generally means that you are trying to use an image that Testcontainers has not been designed to use. If this is deliberate, and if you are confident that the image is compatible, you should declare compatibility in code using the `asCompatibleSubstituteFor` method. For example:
DockerImageName myImage = DockerImageName.parse("mysql/mysql-server:8.0.26").asCompatibleSubstituteFor("mysql");
์๋ฌด๋๋ docker image๋ฅผ mysql์์ mysql-server๋ก ๋ฐ๊พธ๋ ์ ๋ง testcontainers ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ฌ์ฉํ๋ mysql์ด ๋ง๋์ง๋ฅผ ๋ฌผ์ด๋ณด๋ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
๋ฐ๋ผ์ ์๋ฌ์์ ์ค๋ช
ํ๋ ๋๋ก ์ค์ ์ ์์ ํ์๋ค.
DockerImageName.parse("mysql/mysql-server:8.0.26")
.asCompatibleSubstituteFor("mysql")
.let { compatibleImageName -> MySQLContainer<Nothing>(compatibleImageName) }
java.sql.SQLException: null, message from server: "Host '172.17.0.1' is not allowed to connect to this MySQL server"
๊ธฐ์กด์๋ ์ฃผ๋ก postgresql์ ํ์ฉํด์ ์ธ์ง mysql8์์ ํ์ํ ์ต์
๋ค์ ์กฐ๊ธ ๋์ณค๋ ๊ฒ ๊ฐ๋ค.
mysql 8 docker ์ค์ ๋ค์ ๊ตฌ๊ธ๋งํด์ env๋ฅผ ์ถ๊ฐํด์ ์ต์ข
์ ์ผ๋ก ์ฐ๋์ ์ฑ๊ณตํ์๋ค.
companion object {
@JvmStatic
val MY_SQL_CONTAINER: MySQLContainer<*> =
// image for linux/arm64/v8 m1 support
DockerImageName.parse("mysql/mysql-server:8.0.26")
.asCompatibleSubstituteFor("mysql")
.let { compatibleImageName -> MySQLContainer<Nothing>(compatibleImageName) }
.apply {
withDatabaseName(DATABASE_NAME)
withUsername(USERNAME)
withPassword(PASSWORD)
withEnv("MYSQL_USER", USERNAME)
withEnv("MYSQL_PASSWORD", PASSWORD)
withEnv("MYSQL_ROOT_PASSWORD", PASSWORD)
start()
}
const val DATABASE_NAME: String = "batch_test"
const val USERNAME: String = "root"
const val PASSWORD: String = "password"
}
m1์ ์ฐ๋ค๋ณด๋ ์คํ์๋๋ ๋ฐฐํฐ๋ฆฌ ์๊ฐ ๋ฑ ์ป์ ์ด๋์ด ํฌ์ง๋ง ๋ฐ๋๋ก ์์ง ์ํ๊ณ๊ฐ ์กฐ๊ธ ๋ถ์กฑํ ๋ถ๋ถ๋ค์ด ๋จ์ ์ธ ๊ฒ ๊ฐ๋ค. ํ์ง๋ง ๋ ๊ทธ๋ ๋ฏ์ด ํด๊ฒฐ๋ฐฉ์์ ์๊ธฐ ๋ง๋ จ์ด๋ค. ๋!
์ ๋ง ๋ฉ์ ธ์!