Skip to content

Commit 0a0a652

Browse files
authored
feat(be) : 실제 API 응답 테스트 (#43) (#47)
1 parent ef33e03 commit 0a0a652

File tree

1 file changed

+44
-126
lines changed

1 file changed

+44
-126
lines changed
Lines changed: 44 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,157 +1,75 @@
11
package com.back.koreaTravelGuide.domain.ai.tour.client
22

33
import com.back.koreaTravelGuide.KoreaTravelGuideApplication
4-
import com.back.koreaTravelGuide.domain.ai.tour.dto.InternalData
5-
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourResponse
6-
import com.fasterxml.jackson.databind.ObjectMapper
7-
import org.junit.jupiter.api.BeforeEach
4+
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourSearchParams
5+
import org.junit.jupiter.api.Assumptions.assumeTrue
86
import org.junit.jupiter.api.DisplayName
97
import org.junit.jupiter.api.Test
10-
import org.junit.jupiter.api.extension.ExtendWith
118
import org.springframework.beans.factory.annotation.Autowired
129
import org.springframework.beans.factory.annotation.Value
1310
import org.springframework.boot.test.context.SpringBootTest
14-
import org.springframework.boot.test.context.TestConfiguration
15-
import org.springframework.boot.web.client.RestTemplateBuilder
16-
import org.springframework.context.annotation.Bean
17-
import org.springframework.http.HttpMethod
18-
import org.springframework.http.MediaType
1911
import org.springframework.test.context.ActiveProfiles
20-
import org.springframework.test.context.junit.jupiter.SpringExtension
21-
import org.springframework.test.web.client.MockRestServiceServer
22-
import org.springframework.test.web.client.match.MockRestRequestMatchers.method
23-
import org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo
24-
import org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess
25-
import org.springframework.web.client.RestTemplate
26-
import org.springframework.web.util.UriComponentsBuilder
27-
import java.net.URI
28-
import kotlin.test.assertEquals
29-
import kotlin.test.assertNotNull
30-
import kotlin.test.assertNull
12+
import kotlin.test.assertTrue
3113

32-
// 09.26 양현준
33-
@ExtendWith(SpringExtension::class)
34-
// 패키지 경로에서 메인 설정을 찾지 못하는 오류를 해결하기 위해 애플리케이션 클래스를 명시.
14+
/**
15+
* 실제 관광청 API 상태를 확인하기 위한 통합 테스트.
16+
*/
3517
@SpringBootTest(classes = [KoreaTravelGuideApplication::class])
3618
@ActiveProfiles("test")
3719
class TourApiClientTest {
38-
@Autowired private lateinit var restTemplateBuilder: RestTemplateBuilder
39-
40-
@Autowired private lateinit var objectMapper: ObjectMapper
20+
@Autowired
21+
private lateinit var tourApiClient: TourApiClient
4122

4223
@Value("\${tour.api.key}")
4324
private lateinit var serviceKey: String
4425

45-
@Value("\${tour.api.base-url}")
46-
private lateinit var apiUrl: String
47-
48-
private lateinit var restTemplate: RestTemplate
49-
private lateinit var mockServer: MockRestServiceServer
50-
private lateinit var tourApiClient: TourApiClient
51-
52-
// 테스트마다 클라이언트와 Mock 서버를 새로 구성해 호출 상태를 초기화.
53-
@BeforeEach
54-
fun setUp() {
55-
restTemplate = restTemplateBuilder.build()
56-
mockServer = MockRestServiceServer.createServer(restTemplate)
57-
tourApiClient = TourApiClient(restTemplate, objectMapper, serviceKey, apiUrl)
58-
}
59-
60-
@DisplayName("fetchTourInfo - 첫 번째 관광 정보를 반환하는지.")
61-
@Test
62-
fun testReturnsFirstTourInfo() {
63-
val params = InternalData(numOfRows = 2, pageNo = 1, areaCode = "1", sigunguCode = "7")
64-
expectTourRequest(params, responseWithItems(sampleTourItem()))
65-
66-
val result: TourResponse? = tourApiClient.fetchTourInfo(params)
6726

68-
mockServer.verify()
69-
assertNotNull(result)
70-
assertEquals("2591792", result.contentId)
71-
assertEquals("개봉유수지 생태공원", result.title)
72-
assertEquals("7", result.sigunguCode)
73-
}
74-
75-
@DisplayName("fetchTourInfo - item 배열이 비어 있으면 null을 돌려주는지.")
27+
@DisplayName("fetchTourInfo - 실제 관광청 API 호출 (데이터 기대)")
7628
@Test
77-
fun testReturnsNullWhenItemsMissing() {
78-
val params = InternalData(numOfRows = 1, pageNo = 1, areaCode = "1", sigunguCode = "7")
79-
expectTourRequest(params, responseWithItems())
29+
fun fetchTourInfoTest() {
30+
31+
val params =
32+
TourSearchParams(
33+
numOfRows = 1,
34+
pageNo = 1,
35+
contentTypeId = "12",
36+
areaCode = "1",
37+
sigunguCode = "1",
38+
)
8039

8140
val result = tourApiClient.fetchTourInfo(params)
8241

83-
mockServer.verify()
84-
assertNull(result)
85-
}
42+
println("실제 API 응답 아이템 수: ${result.items.size}")
43+
println("첫 번째 아이템: ${result.items.firstOrNull()}")
8644

87-
// 요청 URL과 응답 바디를 미리 세팅해 Mock 서버가 기대한 호출을 검증.
88-
private fun expectTourRequest(
89-
params: InternalData,
90-
responseBody: String,
91-
) {
92-
mockServer.expect(requestTo(buildExpectedUrl(params)))
93-
.andExpect(method(HttpMethod.GET))
94-
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON))
45+
assertTrue(result.items.isNotEmpty(), "실제 API 호출 결과가 비어 있습니다. 장애 여부를 확인하세요.")
9546
}
9647

97-
// 실제 클라이언트가 조합하는 URL과 동일한 형태를 만들어 비교한다.
98-
private fun buildExpectedUrl(params: InternalData): String =
99-
UriComponentsBuilder.fromUri(URI.create(apiUrl))
100-
.path("/areaBasedList2")
101-
.queryParam("serviceKey", serviceKey)
102-
.queryParam("MobileOS", "WEB")
103-
.queryParam("MobileApp", "KoreaTravelGuide")
104-
.queryParam("_type", "json")
105-
.queryParam("numOfRows", params.numOfRows)
106-
.queryParam("pageNo", params.pageNo)
107-
.queryParam("contentTypeId", params.contentTypeId)
108-
.queryParam("areaCode", params.areaCode)
109-
.queryParam("sigunguCode", params.sigunguCode)
110-
.build()
111-
.encode()
112-
.toUriString()
113-
114-
// Jackson을 활용해 테스트 응답 JSON을 손쉽게 조립한다.
115-
private fun responseWithItems(vararg items: Map<String, String>): String {
116-
val response =
117-
mapOf(
118-
"response" to
119-
mapOf(
120-
"header" to mapOf("resultCode" to "0000", "resultMsg" to "OK"),
121-
"body" to
122-
mapOf(
123-
"items" to mapOf("item" to items.toList()),
124-
),
125-
),
48+
@DisplayName("fetchTourInfo - 실제 관광청 API 장애 시 빈 결과 확인")
49+
@Test
50+
fun fetchTourInfoEmptyTest() {
51+
52+
val params =
53+
TourSearchParams(
54+
numOfRows = 1,
55+
pageNo = 1,
56+
contentTypeId = "12",
57+
areaCode = "1",
58+
sigunguCode = "1",
12659
)
12760

128-
return objectMapper.writeValueAsString(response)
129-
}
61+
val result = tourApiClient.fetchTourInfo(params)
62+
63+
println("실제 API 응답 아이템 수: ${result.items.size}")
64+
println("첫 번째 아이템: ${result.items.firstOrNull()}")
13065

131-
// 테스트용 샘플 관광지 정의한다.
132-
private fun sampleTourItem(): Map<String, String> =
133-
mapOf(
134-
"contentid" to "2591792",
135-
"contenttypeid" to "12",
136-
"createdtime" to "20190313221125",
137-
"modifiedtime" to "20250316162225",
138-
"title" to "개봉유수지 생태공원",
139-
"addr1" to "서울특별시 구로구 개봉동",
140-
"areacode" to "1",
141-
"firstimage" to "",
142-
"firstimage2" to "",
143-
"mapx" to "126.8632141714",
144-
"mapy" to "37.4924524597",
145-
"mlevel" to "6",
146-
"sigungucode" to "7",
147-
"lDongRegnCd" to "11",
148-
"lDongSignguCd" to "530",
149-
)
66+
// 장애가 아닐 경우, 테스트를 스킵
67+
assumeTrue(result.items.isEmpty()) {
68+
"API가 정상 응답을 반환하고 있어 장애 시나리오 테스트를 건너뜁니다."
69+
}
15070

151-
// 테스트에서 RestTemplateBuilder 빈을 보장해 컨텍스트 로딩 실패 해결.
152-
@TestConfiguration
153-
class TestConfig {
154-
@Bean
155-
fun restTemplateBuilder(): RestTemplateBuilder = RestTemplateBuilder()
71+
// 장애 상황일 시
72+
println("실제 API가 비어 있는 응답을 반환했습니다.")
73+
assertTrue(result.items.isEmpty())
15674
}
15775
}

0 commit comments

Comments
 (0)