@WireMockTest로 테스트하기
하지만 "말이 싸다, 코드를 보여줘..."😮
좋습니다. 이 시나리오를 구현해 보겠습니다.
로저비나스 / 와이어목 테스트
🤹 WireMock 테스트
BarClient
FooClient
App
BarClient
BarClient 인터페이스
interface BarClient {
fun call(name: String): String
}
BarKtor클라이언트 테스트
나는 Http 클라이언트가 필요한 다른 이유 없이 Ktor client을 사용할 것입니다. Kotlin을 사용하고 있기 때문에 이것은 흥미로워 보입니다.
따라서 BarKtorClient에 대한 간단한 @WireMockTest는 다음과 같습니다.
@WireMockTest
class BarKtorClientShould {
private val name = "Sue"
@Test
fun `call bar api`(wm: WireMockRuntimeInfo) {
stubFor(
get(urlPathMatching("/bar/$name"))
.willReturn(ok().withBody("Hello $name I am Bar!"))
)
assertThat(
BarKtorClient(wm.httpBaseUrl).call(name)
).isEqualTo("Hello $name I am Bar!")
}
@Test
fun `handle bar api server error`(wm: WireMockRuntimeInfo) {
stubFor(
get(urlPathMatching("/bar/.+"))
.willReturn(serverError())
)
assertThat(BarKtorClient(wm.httpBaseUrl).call(name))
.startsWith("Bar api error: Server error")
}
}
BarKtorClient 구현
테스트를 통과하기 위해 🟩 BarKtorClient를 다음과 같이 구현할 수 있습니다.
class BarKtorClient(private val url: String) : BarClient {
private val client = HttpClient(CIO)
override fun call(name: String): String = runBlocking {
try {
client.get("$url/bar/$name")
} catch (e: Exception) {
"Bar api error: ${e.message}"
}
}
}
FooClient
FooClient 인터페이스
interface FooClient {
fun call(name: String): String
}
FooKtor클라이언트 테스트
이 테스트에서는 WireMock's response templating 기능을 사용하고 싶으므로 @WireMockTest를 사용하는 대신 WireMockExtension을 등록합니다.
@TestInstance(PER_CLASS)
class FooKtorClientShould {
private val name = "Joe"
@RegisterExtension
val wm: WireMockExtension = WireMockExtension.newInstance()
.options(wireMockConfig()
.extensions(ResponseTemplateTransformer(true))
)
.configureStaticDsl(true)
.build()
@Test
fun `call foo api`() {
stubFor(
get(urlPathEqualTo("/foo"))
.withQueryParam("name", matching(".+"))
.willReturn(ok().withBody("Hello {{request.query.name}} I am Foo!"))
)
assertThat(FooKtorClient(wm.baseUrl()).call(name))
.isEqualTo("Hello $name I am Foo!")
}
@Test
fun `handle foo api server error`() {
stubFor(
get(urlPathEqualTo("/foo"))
.willReturn(WireMock.serverError())
)
assertThat(FooKtorClient(wm.baseUrl()).call(name))
.startsWith("Foo api error: Server error")
}
}
참고:
name
. @TestInstance(PER_CLASS)
JUnit5가 두 테스트에서 사용할 FooKtorClientShould의 단일 인스턴스를 생성하여 WireMockExtension이 한 번만 등록되도록 합니다. 기본적으로 JUnit5는 각 테스트에 대해 하나의 인스턴스를 만듭니다(Test Instance Lifecycle 참조). configureStaticDsl(true)
를 사용하면 stubFor(...)
대신 wm.stubFor(...)
를 정적으로 사용하는 정적 DSL을 사용할 수 있습니다. FooKtorClient 구현
테스트를 통과하기 위해 이전과 동일 🟩 다음과 같이 FooKtorClient를 구현할 수 있습니다.
class FooKtorClient(private val url: String) : FooClient {
private val client = HttpClient(CIO)
override fun call(name: String): String = runBlocking {
try {
client.get("$url/foo") {
parameter("name", name)
}
} catch (e: Exception) {
"Foo api error: ${e.message}"
}
}
}
AppUseCase
이제 FooClient를 사용하여 Foo API를 호출한 다음 BarClient를 사용하여 Bar API를 호출하는 AppUseCase를 구현해야 합니다.
MockK JUnit5 extension을 사용하여 구현을 먼저 테스트할 수 있기 때문에 WireMock과 관련이 없으므로 세부 정보를 건너뛸 수 있고 AppUseCaseShould 및 AppUseCase의 소스 코드를 검토할 수 있습니다.
앱
앱 구현
나중에 두 가지 다른 유형의 WireMock 테스트를 제시할 것이므로 먼저 앱 구현을 소개하겠습니다.
class App(
private val name: String,
private val fooApiUrl: String,
private val barApiUrl: String
) {
fun execute() = AppUseCase().execute(
name,
FooKtorClient(fooApiUrl),
BarKtorClient(barApiUrl)
)
}
@WireMockTest로 앱 테스트
이 예제에서 Foo API와 Bar API에는 충돌하는 엔드포인트가 없으므로 하나의 @WireMockTest를 사용하여 두 API를 모의할 수 있습니다.
@WireMockTest
class AppShouldWithOneWireMockTest {
private val name = "Ada"
@Test
fun `call foo and bar`(wm: WireMockRuntimeInfo) {
stubFor(
get(urlPathEqualTo("/foo"))
.withQueryParam("name", equalTo(name))
.willReturn(ok().withBody("Hello $name I am Foo!"))
)
stubFor(
get(urlPathMatching("/bar/$name"))
.willReturn(ok().withBody("Hello $name I am Bar!"))
)
val app = App(name, wm.httpBaseUrl, wm.httpBaseUrl)
assertThat(app.execute()).isEqualTo(
"""
Hi! I am $name
I called Foo and its response is Hello $name I am Foo!
I called Bar and its response is Hello $name I am Bar!
Bye!
""".trimIndent()
)
}
}
WireMockExtension으로 앱 테스트
그러나 Foo API와 Bar API가 충돌하는 끝점이 있거나 어떤 이유로든 별도로 모의하려는 실제 시나리오를 상상해 보십시오. 이 경우 @WireMockTest를 사용하는 대신 두 개의 WireMockExtensions를 등록할 수 있습니다.
@TestInstance(PER_CLASS)
class AppShouldWithTwoWireMockExtensions {
private val name = "Leo"
@RegisterExtension
val wireMockFoo: WireMockExtension = newInstance().build()
@RegisterExtension
val wireMockBar: WireMockExtension = newInstance().build()
@Test
fun `call foo and bar`() {
wireMockFoo.stubFor(
get(WireMock.urlPathEqualTo("/foo"))
.withQueryParam("name", equalTo(name))
.willReturn(ok().withBody("Hello $name I am Foo!"))
)
wireMockBar.stubFor(
get(WireMock.urlPathMatching("/bar/$name"))
.willReturn(ok().withBody("Hello $name I am Bar!"))
)
val app = App(name, wireMockFoo.baseUrl(), wireMockBar.baseUrl())
assertThat(app.execute()).isEqualTo(
"""
Hi! I am $name
I called Foo and its response is Hello $name I am Foo!
I called Bar and its response is Hello $name I am Bar!
Bye!
""".trimIndent()
)
}
}
그것은 좋은 것이었다! 즐거운 코딩하세요! 💙
Reference
이 문제에 관하여(@WireMockTest로 테스트하기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/rogervinas/testing-with-wiremocktest-3da0텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)