컨트롤러 코드는
각각 아래와 같이 작성했다.
해당 코드의 테스트코드는 아래와 같이 작성했다.
Mockito 라이브러리와 mock 객체를 사용했다.
<ProductControllerTest>
@SpringBootTest
@AutoConfigureMockMvc
public class ProductControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private Gson gson;
@MockBean
private ProductService productService;
@Test
void postProductTest() throws Exception {
// given
ProductDto.Create productCreateDto = ProductDto.Create.builder()
.name("노트북")
.sellPrice(120)
.buyPrice(100)
.quantity(1000)
.build();
Product createProduct = new Product();
createProduct.setProductId(1L);
given(productService.createProduct(Mockito.any(ProductDto.Create.class)))
.willReturn(createProduct);
String content = gson.toJson(productCreateDto);
// when
ResultActions actions =
mockMvc.perform(
post("/products")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(content)
);
// then
actions
.andExpect(status().isCreated())
.andExpect(header().string("Location", is(startsWith("/products"))));
}
@Test
public void sellProductTest() throws Exception {
// Given
long memberId = 1;
long productId = 1;
int quantity = 10;
Product createProduct = new Product();
createProduct.setProductId(1L); // 생성된 product의 ID 설정
given(productService.sellProduct(Mockito.any(Long.class), Mockito.any(Long.class), Mockito.any(Integer.class)))
.willReturn(createProduct); // 메서드의 동작 정의
// When
ResultActions actions =
mockMvc.perform(patch("/products/sell/{product-id}/{member-id}/{quantity}", productId, memberId, quantity)
.contentType(MediaType.APPLICATION_JSON));
// Then
actions
.andExpect(status().isCreated())
.andExpect(header().string("Location", startsWith("/products")));
}
}
위와 비슷하게
MemberControllerTest
BalanceControllerTest 코드를 작성했다.
passed 16.. 나름 코드 작성하느라 힘들었다.
왜 DDD보다 TDD가 힘들다고 하는지 이해가 간다...
비즈니스로직이나 컨트롤러코드 짜는것보다, 테스트코드 짜는게 시간이 더 걸렸다..
컨트롤러 테스트코드를 작성하며 부딪힌 에러에서 느낀점은,
컨트롤러 코드의 테스트는, 외부와의 API소통이 잘 이루어지는지를 테스트하므로, 서비스로직은 서비스테스트에서 작성할것
MockBean으로 만들어진 객체는, 가짜 객체이므로 실제 객체가 수행하는 로직을 제대로 수행할 수 없다.
(테스트 코드에서 MockBean으로 생성된 객체의 메서드를 호출하면, 실제 객체의 동작이 실행되지 않고 Mock 객체가 설정된 동작을 수행하므로)
이렇게 테스트코드는 다 작성했고, 코드를 실행시켜 잘 저장이 되는지 볼 차례이다.
위 세 이미지를 보면 보시다시피 테이블별로 잘 저장이 되고,
이렇게 1번 member가, 1번 product를 10개 buy한다는 요청을 보내면,
위의 세개의 로직이 작동되어야 한다.
1번 member 초기 result 0
초기 amount 100000000
1번 product 초기 개수 1000, 구매가 100, 사들인 양 10 -> 총액1000 지출
이므로
1번 member 의 result 는 -1000
자산 총액 amount = 100000000 - 1000 = 99999000
1번 product의 quantity는 1000(초기) + 10(사들인 양) = 1010 이 되어야 한다.
amount = 99999000
result = 초기0 - 지출1000 = -1000
quantity = 1010
내가 생각한대로 로직이 잘 짜여진듯 하다.
비록 혼자 만든 재고관리 시스템이지만,
차세대 ERP 등
이제는 관리 시스템조차 AI와 연결된다.
그렇다면...
각각 사원이 실현해낸 이익인 result 필드 등을 기반으로
chatGPT에게 인사에 관해서, 혹은 재고 관련해서 전략을 물어볼 수 있도록
외부 API를 통해 chatGPT와 Spring을 사용하여 연결하려고 한다.
일단, 당연히 외부 API를 이용하기 위해서는 key를 발급받아야 한다.
https://platform.openai.com/account/api-keys
에서
다음과 같이 발급받았다.
발급받은 후에는, 사용해야 하므로
의존성 추가(gradle),
implementation 'io.github.flashvayne:chatgpt-spring-boot-starter:1.0.4'
그다음 받급받은 키를 어플리케이션의 설정파일에 추가해준다.
(application.yml)
chatgpt:
api-key: 발급받은 키를 집어 넣으세용
위의 의존성을 추가해주면서 생긴, ChatgptService 인터페이스를 이용해서,
@Service
@RequiredArgsConstructor
public class ChatService {
private final ChatgptService chatgptService;
public String getChatting (String question) {
return chatgptService.sendMessage(question);
}
}
위와 같이 코드를 짰다.
(ChatServe 클래스가 내가 만든것, ChatgptService가 의존성으로 생긴 인터페이스)
그다음 Controller 클래스는 아래와 같이 만들었다.
@RestController
@Slf4j
@Validated
@Transactional
@RequiredArgsConstructor
@RequestMapping("/chatGPT")
public class ChatController {
private final static String GPT_DEFAULT_URL = "/chatGPT";
private final ChatService chatService;
@PostMapping
public String chatting (@RequestBody String question) {
return chatService.getChatting(question);
}
}
외부 API는 에러나오면 골치아픈 일이 많아서,
제발 돼라돼라 하면서 코드를 실행시켰다.
컨트롤러의 url로 String을 보내면,
보기좋게 위와 같이 성공했다.
재고시스템, 혹은 인사시스템에 도움을 받을 수 있는 인공지능 대화를
chatGpt 의 API를 받아와 추가해줬다!
Java Restdocs 으로 문서화하거나 or AWS 배포를 위해 VM들의 설정을 체크 or redis 설정을 추가해줄 예정이다.
뛰어난 글이네요, 감사합니다.