#MockMvc #Spring_Test ## 참고 : test 패키지 구조 ![[0. ChatGPT 프록시 서버 소개 및 주요 서비스#test 패키지 구조]] ## 참고 : [[D. Spring MockMvc 정보 정리|Spring MockMvc 정보 정리]] 아래의 테스트들은 모두 `MockMvc`를 통해 진행된다. 따라서 `MockMvc`의 작동 방식을 미리 알고 있어야 테스트의 의미를 제대로 이해할 수 있다. 위의 문서는 `MockMvc`의 작동 방식을 따로 정리한 것이다. ## `config` 작성 ```java @TestConfiguration public class ChatGPTControllerTestConfig { @Bean public PromptTemplateRequest templateRequest(){ return PromptTemplateRequest.builder() .id("example") .content("Say '${word1} is ${word2}!'. Only say answer. Do not say anything else.") .build(); } @Bean public Map<String, String> map(){ Map<String, String> map = new HashMap<>(); map.put("word1", "OBSIDIAN"); map.put("word2", "GOOD"); return map; } } ``` - `templateRequest`, `map` : 요청-응답 기능을 테스트하면서 요청 DTO 인스턴스를 계속 사용한다. 사용할 때마다 매번 생성하면 관리하기 힘드니 빈으로 등록한다. ## `ChatGPTControllerTest` 작성 ### 클래스 생성 ```java @AutoConfigureMockMvc @SpringBootTest(webEnvironment = WebEnvironment.MOCK) @TestPropertySource(locations = "classpath:test.properties") @Import(ChatGPTControllerTestConfig.class) public class ChatGPTControllerTest { } ``` - `@AutoConfigureMockMvc`은 이름을 읽어보면 무슨 뜻인지 바로 알 수 있다. `Auto(자동)Configure(설정)MockMvc`, 즉 Configuration으로 빈 등록을 하듯 자동으로 MockMvc를 빈으로 등록해주는 어노테이션이다. - `@AutoConfigureMockMvc`을 안 쓰고 따로 `MockMvc`를 `ChatGPTControllerTestConfig`에서 빈으로 등록하는 방법도 가능하다. 다만, 이 경우 컨트롤러에 의존성 주입이 안 되기 때문에 따로 의존성 주입을 해줘야 한다. - `WebEnvironment.MOCK`은 모의 웹 환경으로 테스트를 진행한다는 뜻으로 실제 서블릿 컨테이너가 실행되는 걸 막는다. - 나머지 어노테이션을 사용하는 이유는 이전의 테스트 코드와 동일하다. ### 의존성 주입 ```java @AutoConfigureMockMvc @SpringBootTest(webEnvironment = WebEnvironment.MOCK) @TestPropertySource(locations = "classpath:test.properties") @Import(ChatGPTControllerTestConfig.class) public class ChatGPTControllerTest { @Autowired private MockMvc mockMvc; @Autowired private PromptTemplateRequest templateRequest; @Autowired private Map<String, String> map; @Autowired private PromptTemplateService service; } ``` - `@AutoConfigureMockMvc`을 통해 생성된 `MockMvc` 빈을 주입 받는다. - `POST` 이외에 다른 메서드(`GET`, `PUT`, `DELETE`)를 테스트하기 위해서는 일단 데이터베이스 안에 데이터가 있어야 한다. 그래서 `PromptTemplateService`를 주입 받아 테스트 전에 데이터를 집어 넣는 용도로 사용한다. ### 메서드 작성 #### 주입 여부 확인 ```java @Test public void testMockMvcExistence(){ assertNotNull(mockMvc); assertNotNull(templateRequest); assertNotNull(map); assertNotNull(service); } ``` #### API를 통한 템플릿 CRUD 테스트 ##### POST 테스트 ```java @Test public void testPostPromptTemplate() throws Exception { mockMvc.perform(post("/prompt-template") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(templateRequest))) .andExpect(status().isCreated()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.id").value(templateRequest.getId())) .andExpect( jsonPath("$.content").value(templateRequest.getContent()) ); } ``` ##### GET 테스트 ```java @Test public void testGetPromptTemplateById() throws Exception { service.create(templateRequest.toEntity()); mockMvc.perform(get("/prompt-template/%s" .formatted(templateRequest.getId()))) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.id").value(templateRequest.getId())) .andExpect( jsonPath("$.content").value(templateRequest.getContent()) ); } ``` ##### PUT 테스트 ```java @Test public void testPutPromptTemplateById() throws Exception { service.create(templateRequest.toEntity()); templateRequest.setContent("Say '${word1} is NOT ${word2}!!!'. Only say answer. Do not say anything else."); mockMvc.perform(put("/prompt-template/%s" .formatted(templateRequest.getId())) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(templateRequest))) .andExpect(status().isCreated()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.id").value(templateRequest.getId())) .andExpect( jsonPath("$.content").value(templateRequest.getContent()) ); } ``` ##### DELETE 테스트 ```java @Test public void testDeletePromptTemplateById() throws Exception { service.create(templateRequest.toEntity()); mockMvc.perform(delete("/prompt-template/%s" .formatted(templateRequest.getId()))) .andExpect(status().isOk()); } ``` #### ChatGPT API 프록시 테스트 ##### `sendPrompt` 테스트 ```java @Test public void testSendPrompt() throws Exception { mockMvc.perform(post("/prompt") .contentType(MediaType.TEXT_PLAIN) .content("Say 'OBSIDIAN'. Only say answer. Do not say anything else.")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.TEXT_PLAIN_VALUE + ";charset=UTF-8")) .andExpect(content().string("OBSIDIAN")); } ``` - [[4-2. 클라이언트 테스트 코드#ChatGPT API 요청 기능 테스트|클라이언트 테스트 코드]]와 유사하다. 프롬프트를 플레인 텍스트 타입으로 바디에 넣어서 요청을 보내면, ChatGPT API의 결과를 서버가 반환하는지 확인하는 테스트이다. ##### `sendPromptByTemplate` 테스트 ```java @Test public void testSendPromptByTemplate() throws Exception { templateRequest.setContent("Say '${word1} is NOT ${word2}!!!'. Only say answer. Do not say anything else."); service.create(templateRequest.toEntity()); mockMvc.perform( post("/prompt/generated-by/prompt-template/%s".formatted(templateRequest.getId())) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(map))) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.TEXT_PLAIN_VALUE + ";charset=UTF-8")) .andExpect(content().string("OBSIDIAN is NOT GOOD!!!")); } ``` - 이 또한 [[4-2. 클라이언트 테스트 코드#템플릿을 통한 ChatGPT API 요청 기능 테스트|클라이언트 테스트 코드]]와 유사하다. ## [[8. 컨트롤러 구현(API)|컨트롤러 코드 작성]] ## 테스트 결과 확인 ![[Pasted image 20231215100648.png]]