프로젝트를 하다 보면 API Key와 같은 민감한 정보들을 따로 관리하는 파일에 저장하고 앱에서 불러와 사용하는 경우가 있다.
테스트 코드에서 이를 불러오는 테스트를 작성해 보았다.
case class MyConfig(value: Option[String])
object MyConfig {
val config: Config[MyConfig] = DeriveConfig.deriveConfig[MyConfig].nested("my", "env")
}
object ConfigSpec extends ZIOSpecDefault {
val testConfigProviderFromMap: ZLayer[Any, Nothing, Unit] =
Runtime.setConfigProvider(
ConfigProvider
.fromMap(Map("my.env.value" -> "this is from map")))
override def spec: Spec[TestEnvironment with Scope, Any] =
suite("config from map")(
test("fetch value from map") {
for {
config <- ZIO.config[MyConfig](MyConfig.config)
} yield assertTrue(config.value.contains("this is from map"))
}
).provideLayer(testConfigProviderFromMap)
}
위의 코드에서 ConfigProvider
를 test suite 에 provideLayer
를 통해 제공해 준다.
ConfigProvider
는 .fromMap()
메서드에 Map 형태로 세팅해서 사용할 수 있다.
# conf/application.conf
my.env {
value = "I am a value from conf/application.conf"
}
# test/resources/application.conf
my.env {
value2 = "I am a value from test/resources/application.conf"
}
# main/resources/application.conf
my.env {
value3 = "I am a value from main/resources/application.conf"
}
프로젝트 내의 각 경로에 위와 같이 .conf 파일을 준비해둔다.
case class MyConfig(value: Option[String], value2: Option[String], value3: Option[String], nothing: Option[String])
object MyConfig {
val config: Config[MyConfig] = DeriveConfig.deriveConfig[MyConfig].nested("my", "env")
}
object ConfigSpec extends ZIOSpecDefault {
private val configProvider: ConfigProvider = TypesafeConfigProvider
.fromHoconFilePath("conf/application.conf") // 여기에서 먼저 찾고
.orElse(TypesafeConfigProvider.fromResourcePath()) // 그 다음 여기에서 찾기
val testConfigProviderFromConfFile: ZLayer[Any, Nothing, Unit] =
Runtime.setConfigProvider(configProvider)
override def spec: Spec[TestEnvironment with Scope, Any] =
suite("config from conf file")(
test("fetch value from conf/application.conf") {
for {
config <- ZIO.config[MyConfig](MyConfig.config)
} yield assertTrue(config.value.contains("I am a value from conf/application.conf"))
},
test("fetch value from test/resources/application.conf") {
for {
config <- ZIO.config[MyConfig](MyConfig.config)
} yield assertTrue(config.value2.contains("I am a value from test/resources/application.conf"))
},
test("fetch value from main/resources/application.conf") {
for {
config <- ZIO.config[MyConfig](MyConfig.config)
} yield assertTrue(config.value3.contains("I am a value from main/resources/application.conf"))
},
test("fetch value doesn't exist") {
for {
config <- ZIO.config[MyConfig](MyConfig.config)
} yield assertTrue(config.nothing.isEmpty)
},
).provideLayer(testConfigProviderFromConfFile)
}
테스트코드를 보면 config 값을 찾는 순서는 아래와 같다.
1. configProvider 에 지정해 둔 대로 "conf/application.conf" 에서 먼저 값을 찾는다.
2. 없으면 "test/resources/application.conf" 에서 값을 찾는다.
3. 없으면 "main/resources/application.conf" 에서 값을 찾는다.
4. 없으면 없는것으로 핸들링한다.
테스트 코드에서 환경변수를 원하는 대로 세팅해서 테스트의 자유도를 높일 수 있게 되었다.