Skip to content

Commit 9edd3fa

Browse files
committed
refactor(be) : 실제 api 호출 테스트 제거
1 parent 32cfb71 commit 9edd3fa

File tree

1 file changed

+88
-115
lines changed

1 file changed

+88
-115
lines changed

src/test/kotlin/com/back/koreaTravelGuide/domain/ai/tour/client/TourApiClientTest.kt

Lines changed: 88 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@ import com.back.koreaTravelGuide.KoreaTravelGuideApplication
44
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourParams
55
import com.fasterxml.jackson.databind.ObjectMapper
66
import org.junit.jupiter.api.AfterEach
7-
import org.junit.jupiter.api.Assumptions.assumeTrue
87
import org.junit.jupiter.api.BeforeEach
98
import org.junit.jupiter.api.DisplayName
109
import org.junit.jupiter.api.Nested
1110
import org.junit.jupiter.api.Test
12-
import org.springframework.beans.factory.annotation.Autowired
1311
import org.springframework.boot.test.context.SpringBootTest
1412
import org.springframework.http.HttpMethod
1513
import org.springframework.http.HttpStatus
@@ -26,139 +24,114 @@ import org.springframework.web.util.UriComponentsBuilder
2624
import kotlin.test.assertEquals
2725
import kotlin.test.assertTrue
2826

29-
// 실제 API 호출 기반 단위 테스트
3027
@SpringBootTest(classes = [KoreaTravelGuideApplication::class])
3128
@ActiveProfiles("test")
32-
class TourApiClientTest
33-
@Autowired
34-
constructor(
35-
private val tourApiClient: TourApiClient,
36-
) {
37-
@DisplayName("fetchTourInfo - 실제 관광청 API가 빈 응답을 줄 경우")
29+
class TourApiClientTest {
30+
// MockRestServiceServer 기반 단위 테스트
31+
@Nested
32+
inner class MockServerTests {
33+
private lateinit var restTemplate: RestTemplate
34+
private lateinit var mockServer: MockRestServiceServer
35+
private lateinit var mockClient: TourApiClient
36+
37+
private val serviceKey = "test-service-key"
38+
private val baseUrl = "https://example.com"
39+
40+
@BeforeEach
41+
fun setUp() {
42+
restTemplate = RestTemplate()
43+
mockServer = MockRestServiceServer.createServer(restTemplate)
44+
mockClient = TourApiClient(restTemplate, ObjectMapper(), serviceKey, baseUrl)
45+
}
46+
47+
@AfterEach
48+
fun tearDown() {
49+
mockServer.verify()
50+
}
51+
52+
@DisplayName("fetchTourInfo - 외부 API가 정상 응답을 반환하면 파싱된 결과를 제공")
3853
@Test
39-
fun fetchTourInfoRealCallEmptyResponse() {
54+
fun fetchTourInfoReturnsParsedItems() {
4055
val params =
4156
TourParams(
4257
contentTypeId = "12",
4358
areaCode = "1",
4459
sigunguCode = "1",
4560
)
4661

47-
val result = tourApiClient.fetchTourInfo(params)
62+
mockServer.expect(ExpectedCount.once(), requestTo(expectedAreaBasedListUrl(params)))
63+
.andExpect(method(HttpMethod.GET))
64+
.andRespond(withSuccess(SUCCESS_RESPONSE, MediaType.APPLICATION_JSON))
4865

49-
// isEmpty가 true인 경우 테스트를 진행, 아닐 경우 메세지 출력
50-
assumeTrue(result.items.isEmpty()) {
51-
"관광청 API가 정상 데이터를 제공하고 있어 장애 시나리오 테스트를 건너뜁니다."
52-
}
66+
val result = mockClient.fetchTourInfo(params)
5367

54-
assertTrue(result.items.isEmpty())
68+
assertEquals(1, result.items.size)
69+
val firstItem = result.items.first()
70+
assertEquals("12345", firstItem.contentId)
71+
assertEquals("테스트 타이틀", firstItem.title)
5572
}
5673

57-
// MockRestServiceServer 기반 단위 테스트
58-
@Nested
59-
inner class MockServerTests {
60-
private lateinit var restTemplate: RestTemplate
61-
private lateinit var mockServer: MockRestServiceServer
62-
private lateinit var mockClient: TourApiClient
63-
64-
private val serviceKey = "test-service-key"
65-
private val baseUrl = "https://example.com"
66-
67-
@BeforeEach
68-
fun setUp() {
69-
restTemplate = RestTemplate()
70-
mockServer = MockRestServiceServer.createServer(restTemplate)
71-
mockClient = TourApiClient(restTemplate, ObjectMapper(), serviceKey, baseUrl)
72-
}
73-
74-
@AfterEach
75-
fun tearDown() {
76-
mockServer.verify()
77-
}
78-
79-
@DisplayName("fetchTourInfo - 외부 API가 정상 응답을 반환하면 파싱된 결과를 제공")
80-
@Test
81-
fun fetchTourInfoReturnsParsedItems() {
82-
val params =
83-
TourParams(
84-
contentTypeId = "12",
85-
areaCode = "1",
86-
sigunguCode = "1",
87-
)
88-
89-
mockServer.expect(ExpectedCount.once(), requestTo(expectedAreaBasedListUrl(params)))
90-
.andExpect(method(HttpMethod.GET))
91-
.andRespond(withSuccess(SUCCESS_RESPONSE, MediaType.APPLICATION_JSON))
92-
93-
val result = mockClient.fetchTourInfo(params)
94-
95-
assertEquals(1, result.items.size)
96-
val firstItem = result.items.first()
97-
assertEquals("12345", firstItem.contentId)
98-
assertEquals("테스트 타이틀", firstItem.title)
99-
}
100-
101-
@DisplayName("fetchTourInfo - 외부 API가 404를 반환하면 빈 결과를 전달")
102-
@Test
103-
fun fetchTourInfoReturnsEmptyListWhenApiFails() {
104-
val params =
105-
TourParams(
106-
contentTypeId = "12",
107-
areaCode = "1",
108-
sigunguCode = "1",
109-
)
110-
111-
mockServer.expect(ExpectedCount.once(), requestTo(expectedAreaBasedListUrl(params)))
112-
.andExpect(method(HttpMethod.GET))
113-
.andRespond(withStatus(HttpStatus.NOT_FOUND))
74+
@DisplayName("fetchTourInfo - 외부 API가 404를 반환하면 빈 결과를 전달")
75+
@Test
76+
fun fetchTourInfoReturnsEmptyListWhenApiFails() {
77+
val params =
78+
TourParams(
79+
contentTypeId = "12",
80+
areaCode = "1",
81+
sigunguCode = "1",
82+
)
11483

115-
val result = mockClient.fetchTourInfo(params)
84+
mockServer.expect(ExpectedCount.once(), requestTo(expectedAreaBasedListUrl(params)))
85+
.andExpect(method(HttpMethod.GET))
86+
.andRespond(withStatus(HttpStatus.NOT_FOUND))
11687

117-
assertTrue(result.items.isEmpty())
118-
}
88+
val result = mockClient.fetchTourInfo(params)
11989

120-
private fun expectedAreaBasedListUrl(params: TourParams): String =
121-
UriComponentsBuilder.fromUriString(baseUrl)
122-
.path("/areaBasedList2")
123-
.queryParam("serviceKey", serviceKey)
124-
.queryParam("MobileOS", "WEB")
125-
.queryParam("MobileApp", "KoreaTravelGuide")
126-
.queryParam("_type", "json")
127-
.queryParam("contentTypeId", params.contentTypeId)
128-
.queryParam("areaCode", params.areaCode)
129-
.queryParam("sigunguCode", params.sigunguCode)
130-
.build()
131-
.encode()
132-
.toUriString()
90+
assertTrue(result.items.isEmpty())
13391
}
13492

135-
companion object {
136-
private val SUCCESS_RESPONSE =
137-
"""
138-
{
139-
"response": {
140-
"header": {
141-
"resultCode": "0000",
142-
"resultMsg": "OK"
143-
},
144-
"body": {
145-
"items": {
146-
"item": [
147-
{
148-
"contentid": "12345",
149-
"contenttypeid": "12",
150-
"createdtime": "202310010000",
151-
"modifiedtime": "202310020000",
152-
"title": "테스트 타이틀",
153-
"addr1": "서울특별시 종로구",
154-
"areacode": "1",
155-
"firstimage": "https://example.com/image.jpg"
156-
}
157-
]
93+
private fun expectedAreaBasedListUrl(params: TourParams): String =
94+
UriComponentsBuilder.fromUriString(baseUrl)
95+
.path("/areaBasedList2")
96+
.queryParam("serviceKey", serviceKey)
97+
.queryParam("MobileOS", "WEB")
98+
.queryParam("MobileApp", "KoreaTravelGuide")
99+
.queryParam("_type", "json")
100+
.queryParam("contentTypeId", params.contentTypeId)
101+
.queryParam("areaCode", params.areaCode)
102+
.queryParam("sigunguCode", params.sigunguCode)
103+
.build()
104+
.encode()
105+
.toUriString()
106+
}
107+
108+
companion object {
109+
private val SUCCESS_RESPONSE =
110+
"""
111+
{
112+
"response": {
113+
"header": {
114+
"resultCode": "0000",
115+
"resultMsg": "OK"
116+
},
117+
"body": {
118+
"items": {
119+
"item": [
120+
{
121+
"contentid": "12345",
122+
"contenttypeid": "12",
123+
"createdtime": "202310010000",
124+
"modifiedtime": "202310020000",
125+
"title": "테스트 타이틀",
126+
"addr1": "서울특별시 종로구",
127+
"areacode": "1",
128+
"firstimage": "https://example.com/image.jpg"
158129
}
159-
}
130+
]
160131
}
161132
}
162-
""".trimIndent()
163-
}
133+
}
134+
}
135+
""".trimIndent()
164136
}
137+
}

0 commit comments

Comments
 (0)