@@ -29,135 +29,136 @@ import kotlin.test.assertTrue
2929// 실제 API 호출 기반 단위 테스트
3030@SpringBootTest(classes = [KoreaTravelGuideApplication ::class ])
3131@ActiveProfiles(" test" )
32- class TourApiClientTest @Autowired constructor(
33- private val tourApiClient : TourApiClient ,
34- ) {
35- @DisplayName(" fetchTourInfo - 실제 관광청 API가 빈 응답을 줄 경우" )
36- @Test
37- fun fetchTourInfoRealCallEmptyResponse () {
38- val params =
39- TourParams (
40- contentTypeId = " 12" ,
41- areaCode = " 1" ,
42- sigunguCode = " 1" ,
43- )
44-
45- val result = tourApiClient.fetchTourInfo(params)
46-
47- // isEmpty가 true인 경우 테스트를 진행, 아닐 경우 메세지 출력
48- assumeTrue(result.items.isEmpty()) {
49- " 관광청 API가 정상 데이터를 제공하고 있어 장애 시나리오 테스트를 건너뜁니다."
50- }
51-
52- assertTrue(result.items.isEmpty())
53- }
54-
55- // MockRestServiceServer 기반 단위 테스트
56- @Nested
57- inner class MockServerTests {
58- private lateinit var restTemplate: RestTemplate
59- private lateinit var mockServer: MockRestServiceServer
60- private lateinit var mockClient: TourApiClient
61-
62- private val serviceKey = " test-service-key"
63- private val baseUrl = " https://example.com"
64-
65- @BeforeEach
66- fun setUp () {
67- restTemplate = RestTemplate ()
68- mockServer = MockRestServiceServer .createServer(restTemplate)
69- mockClient = TourApiClient (restTemplate, ObjectMapper (), serviceKey, baseUrl)
70- }
71-
72- @AfterEach
73- fun tearDown () {
74- mockServer.verify()
75- }
76-
77- @DisplayName(" fetchTourInfo - 외부 API가 정상 응답을 반환하면 파싱된 결과를 제공" )
32+ class TourApiClientTest
33+ @Autowired
34+ constructor (
35+ private val tourApiClient: TourApiClient ,
36+ ) {
37+ @DisplayName(" fetchTourInfo - 실제 관광청 API가 빈 응답을 줄 경우" )
7838 @Test
79- fun fetchTourInfoReturnsParsedItems () {
39+ fun fetchTourInfoRealCallEmptyResponse () {
8040 val params =
8141 TourParams (
8242 contentTypeId = " 12" ,
8343 areaCode = " 1" ,
8444 sigunguCode = " 1" ,
8545 )
8646
87- mockServer.expect(ExpectedCount .once(), requestTo(expectedAreaBasedListUrl(params)))
88- .andExpect(method(HttpMethod .GET ))
89- .andRespond(withSuccess(SUCCESS_RESPONSE , MediaType .APPLICATION_JSON ))
47+ val result = tourApiClient.fetchTourInfo(params)
9048
91- val result = mockClient.fetchTourInfo(params)
49+ // isEmpty가 true인 경우 테스트를 진행, 아닐 경우 메세지 출력
50+ assumeTrue(result.items.isEmpty()) {
51+ " 관광청 API가 정상 데이터를 제공하고 있어 장애 시나리오 테스트를 건너뜁니다."
52+ }
9253
93- assertEquals(1 , result.items.size)
94- val firstItem = result.items.first()
95- assertEquals(" 12345" , firstItem.contentId)
96- assertEquals(" 테스트 타이틀" , firstItem.title)
54+ assertTrue(result.items.isEmpty())
9755 }
9856
99- @DisplayName(" fetchTourInfo - 외부 API가 404를 반환하면 빈 결과를 전달" )
100- @Test
101- fun fetchTourInfoReturnsEmptyListWhenApiFails () {
102- val params =
103- TourParams (
104- contentTypeId = " 12" ,
105- areaCode = " 1" ,
106- sigunguCode = " 1" ,
107- )
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+ }
10873
109- mockServer.expect(ExpectedCount .once(), requestTo(expectedAreaBasedListUrl(params)))
110- .andExpect(method(HttpMethod .GET ))
111- .andRespond(withStatus(HttpStatus .NOT_FOUND ))
74+ @AfterEach
75+ fun tearDown () {
76+ mockServer.verify()
77+ }
11278
113- val result = mockClient.fetchTourInfo(params)
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+ }
114100
115- assertTrue(result.items.isEmpty())
116- }
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+ )
117110
118- private fun expectedAreaBasedListUrl (params : TourParams ): String =
119- UriComponentsBuilder .fromUriString(baseUrl)
120- .path(" /areaBasedList2" )
121- .queryParam(" serviceKey" , serviceKey)
122- .queryParam(" MobileOS" , " WEB" )
123- .queryParam(" MobileApp" , " KoreaTravelGuide" )
124- .queryParam(" _type" , " json" )
125- .queryParam(" contentTypeId" , params.contentTypeId)
126- .queryParam(" areaCode" , params.areaCode)
127- .queryParam(" sigunguCode" , params.sigunguCode)
128- .build()
129- .encode()
130- .toUriString()
111+ mockServer.expect(ExpectedCount .once(), requestTo(expectedAreaBasedListUrl(params)))
112+ .andExpect(method(HttpMethod .GET ))
113+ .andRespond(withStatus(HttpStatus .NOT_FOUND ))
131114
132- }
115+ val result = mockClient.fetchTourInfo(params)
116+
117+ assertTrue(result.items.isEmpty())
118+ }
133119
134- companion object {
135- private val SUCCESS_RESPONSE =
136- """
137- {
138- "response": {
139- "header": {
140- "resultCode": "0000",
141- "resultMsg": "OK"
142- },
143- "body": {
144- "items": {
145- "item": [
146- {
147- "contentid": "12345",
148- "contenttypeid": "12",
149- "createdtime": "202310010000",
150- "modifiedtime": "202310020000",
151- "title": "테스트 타이틀",
152- "addr1": "서울특별시 종로구",
153- "areacode": "1",
154- "firstimage": "https://example.com/image.jpg"
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()
133+ }
134+
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+ ]
155158 }
156- ]
159+ }
157160 }
158161 }
159- }
160- }
161- """ .trimIndent()
162+ """ .trimIndent()
163+ }
162164 }
163- }
0 commit comments