#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]]