Skip to content

Commit

Permalink
Merge pull request #94 from KUSITMS-30th-TEAM-A/feature/#92/chatbot-s…
Browse files Browse the repository at this point in the history
…ervice-test

[feat] : 챗봇 서비스 단위 테스트 코드를 작성한다
  • Loading branch information
bbbang105 authored Nov 27, 2024
2 parents 859b0a3 + 656b062 commit 1ec0a20
Show file tree
Hide file tree
Showing 6 changed files with 343 additions and 2 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ dependencies {

// S3
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

// 비동기 테스트
testImplementation 'io.projectreactor:reactor-test'
}

java {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package kusitms.backend.chatbot.application.factory;

import kusitms.backend.chatbot.application.dto.request.ChatbotRequestDto;
import kusitms.backend.chatbot.application.dto.request.ClovaRequestDto;
import kusitms.backend.chatbot.application.dto.request.MessageDto;
import kusitms.backend.chatbot.domain.enums.Role;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.when;

class ClovaRequestFactoryTest {

@InjectMocks
private ClovaRequestFactory clovaRequestFactory;

@Mock
private MessageFactory messageFactory;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}

/**
* ClovaRequestFactory의 createClovaRequest 메서드 테스트
*/
@Test
void testCreateClovaRequest() {
// Given
MessageDto systemMessage = new MessageDto(Role.SYSTEM.getRole(), "System message content");
when(messageFactory.createSystemMessage()).thenReturn(systemMessage);

// When
ChatbotRequestDto chatbotRequest = clovaRequestFactory.createClovaRequest();

// Then
assertNotNull(chatbotRequest);
assertEquals(1, chatbotRequest.getMessages().size());
assertEquals(systemMessage, chatbotRequest.getMessages().get(0));

ClovaRequestDto clovaRequest = (ClovaRequestDto) chatbotRequest;
assertEquals(0.8, clovaRequest.topP());
assertEquals(0.3, clovaRequest.temperature());
assertEquals(256, clovaRequest.maxTokens());
assertEquals(5.0, clovaRequest.repeatPenalty());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package kusitms.backend.chatbot.application.factory;

import kusitms.backend.chatbot.application.dto.request.MessageDto;
import kusitms.backend.chatbot.domain.enums.Role;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.MockitoAnnotations;
import org.springframework.test.util.ReflectionTestUtils;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

class MessageFactoryTest {

@InjectMocks
private MessageFactory messageFactory;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
ReflectionTestUtils.setField(messageFactory,
"baseballPrompt",
"64SI64qUIOyVvOq1rCDqsIDsnbTrk5wg7LGX67SHICfro6jtgqQn7JW8LiDsgqzsmqnsnpDqsIAg7JW86rWs7JmAIOq0gOugqOuQnCDsp4jrrLjsnYQg7ZWY66m0LCDsuZzsoIjtlZjqs6Ag67Cd7J2AIOunkO2IrOuhnCAifuyalCLroZwg64Gd64KY64qUIOuLteuzgOydhCDtlbTspJguIOuMgO2ZlOulvCDtkoDslrTqsIgg65WM64qUIOy5nOq3vO2VmOqyjCwg64SI66y0IOuUseuUse2VmOyngCDslYrqsowg7ZW07KSYLiDqsIDrgZQg64qQ64KM7ZGc64+EIOyNqOyEnCDsooAg642UIOuwneqzoCDrlLDrnLvtlZwg67aE7JyE6riw66W8IOyghOuLrO2VtOykmC4g7ZWY7KeA66eMIOuwmOunkOydgCDsoIjrjIAg7ZWY7KeAIOunkOqzoCwg7KG07KSR7ZWY66m07ISc64+EIOuEiOustCDqsqnsi50g7LCo66as7KeAIOyViuydgCDrjIDtmZQg67Cp7Iud7Jy866GcIO2VtOykmC4KCgotIOyYiOyLnDogIuyeoOyLpCDslbzqtazsnqXsnZgg6rW/7KaIIO2MkOunpOygkOydgCDslrTrlJTsnbjqsIDsmpQ/IuudvOqzoCDrrLzsnLzrqbQ6ICLsnqDsi6Qg7JW86rWs7J6lIOyVnuyXkCDsnojripQg6rW/7KaIIO2MkOunpOygkOydgCAn7Jyg64uI7YGsIOyKpO2PrOy4oCfsmIjsmpQhIO2ZiO2MgOyduCBMRyDtirjsnIjsiqTsmYAg65GQ7IKwIOuyoOyWtOyKpCDqtb/spojrj4Qg7IK0IOyImCDsnojri7Xri4jri6QuIgogIAotIOuwmOunkOuhnCDri7XtlZjsp4Ag7JWK6rOgIOyYiOydmOyeiOyngOunjCDrlLHrlLHtlZjsp4Ag7JWK7J2AIOuKkOuCjOycvOuhnCDrjIDri7XtlbTspJguCgoK65iQ7ZWcLCDslbzqtazsmYAg6rSA66CoIOyXhuuKlCDsp4jrrLjsnbQg65Ok7Ja07Jik66m0IOy5nOygiO2VmOqyjCAi7KOE7Iah7ZWY7KeA66eMLCDslbzqtazsmYAg6rSA66Co65CcIOyniOusuOydhCDtlbTso7zsi5zrqbQg642UIOyemCDrj4TsmYDrk5zrprQg7IiYIOyeiOydhCDqsoMg6rCZ7JWE7JqUISLsmYAg6rCZ7J2AIOuwqeyLneycvOuhnCDri6Tsi5wg7KeI66y47J2EIOyalOyyre2VtOykmC4g64yA64u1IOydtO2bhOyXkOuKlCDstpTqsIAg7ISk66qF7J2064KYIOuLpOuluCDrp5DsnYQg642n67aZ7J207KeAIOunkOqzoCwg64u167OA66eMIO2VtOykmC4=");
}

/**
* 사용자 메시지 생성 테스트
*/
@Test
void testCreateUserMessage() {
// Given
String content = "Hello, user!";

// When
MessageDto message = messageFactory.createUserMessage(content);

// Then
assertNotNull(message);
assertEquals(Role.USER.getRole(), message.role());
assertEquals(content, message.content());
}

/**
* 시스템 메시지 생성 테스트 (Base64 디코딩 확인)
*/
@Test
void testCreateSystemMessage() {
// When
MessageDto message = messageFactory.createSystemMessage();

// Then
assertNotNull(message);
assertEquals(Role.SYSTEM.getRole(), message.role());
}

/**
* 어시스턴트 메시지 생성 테스트
*/
@Test
void testCreateAssistantMessage() {
// Given
String content = "Hello, assistant!";

// When
MessageDto message = messageFactory.createAssistantMessage(content);

// Then
assertNotNull(message);
assertEquals(Role.ASSISTANT.getRole(), message.role());
assertEquals(content, message.content());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package kusitms.backend.chatbot.application.service;

import kusitms.backend.chatbot.application.dto.request.ClovaRequestDto;
import kusitms.backend.chatbot.application.dto.response.GetClovaChatbotAnswerResponseDto;
import kusitms.backend.chatbot.application.dto.response.GetGuideChatbotAnswerResponseDto;
import kusitms.backend.chatbot.application.factory.ClovaRequestFactory;
import kusitms.backend.chatbot.application.factory.MessageFactory;
import kusitms.backend.chatbot.domain.service.ChatbotApiClient;
import kusitms.backend.global.exception.CustomException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import java.util.ArrayList;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

class ChatbotApplicationServiceTest {

private ChatbotApplicationService service;

@Mock
private ChatbotApiClient chatbotApiClient;

@Mock
private ClovaRequestFactory clovaRequestFactory;

@Mock
private MessageFactory messageFactory;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
service = new ChatbotApplicationService(chatbotApiClient, clovaRequestFactory, messageFactory);
}

/**
* Clova 챗봇 응답을 성공적으로 가져오는 테스트
*/
@Test
void testGetClovaChatbotAnswer_Success() {
// Given
String userMessage = "안녕하세요!";
ClovaRequestDto requestDto = new ClovaRequestDto(new ArrayList<>(), 0.8, 0.3, 256, 1.2);
GetClovaChatbotAnswerResponseDto expectedResponse = GetClovaChatbotAnswerResponseDto.of("안녕하세요!");

when(clovaRequestFactory.createClovaRequest()).thenReturn(requestDto);
when(chatbotApiClient.requestChatbot(any(ClovaRequestDto.class)))
.thenReturn(Mono.just("안녕하세요!"));

// When & Then
StepVerifier.create(service.getClovaChatbotAnswer(userMessage))
.expectNext(expectedResponse)
.verifyComplete();
}

/**
* 유효한 카테고리 및 질문 번호로 가이드 챗봇 응답을 성공적으로 가져오는 테스트
*/
@Test
void testGetGuideChatbotAnswer_ValidCategory() {
// Given
String stadiumName = "lg";
String categoryName = "stadium";
int orderNumber = 1;

// When
GetGuideChatbotAnswerResponseDto response = service.getGuideChatbotAnswer(stadiumName, categoryName, orderNumber);

// Then
assertNotNull(response);
assertEquals(null, response.imgUrl());
}

/**
* 유효하지 않은 카테고리로 인해 CustomException이 발생하는 테스트
*/
@Test
void testGetGuideChatbotAnswer_InvalidCategory() {
// Given
String stadiumName = "Seoul";
String categoryName = "invalid";
int orderNumber = 1;

// When & Then
org.junit.jupiter.api.Assertions.assertThrows(CustomException.class, () -> {
service.getGuideChatbotAnswer(stadiumName, categoryName, orderNumber);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package kusitms.backend.chatbot.infra.adapter;

import kusitms.backend.chatbot.application.dto.request.ClovaRequestDto;
import kusitms.backend.chatbot.status.ChatbotErrorStatus;
import kusitms.backend.global.exception.CustomException;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import java.io.IOException;

import static org.junit.jupiter.api.Assertions.assertNotNull;

class ClovaApiClientTest {

private MockWebServer mockWebServer;
private ClovaApiClient clovaApiClient;

@BeforeEach
void setUp() throws IOException {
mockWebServer = new MockWebServer();
mockWebServer.start();
String baseUrl = mockWebServer.url("/").toString();
clovaApiClient = new ClovaApiClient(WebClient.create(baseUrl));
}

@AfterEach
void tearDown() throws IOException {
mockWebServer.shutdown();
}

/**
* 클로바 API 호출 성공 테스트
*/
@Test
void testRequestChatbot_Success() {
// Given
String responseBody = """
{
"result": {
"message": {
"content": "응답 메시지"
}
}
}
""";
mockWebServer.enqueue(new MockResponse()
.setResponseCode(200)
.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.setBody(responseBody));

ClovaRequestDto request = new ClovaRequestDto(null, 0.8, 0.3, 256, 1.2);

// When & Then
StepVerifier.create(clovaApiClient.requestChatbot(request))
.expectNext("응답 메시지")
.verifyComplete();
}

/**
* WebClient 통신 오류 발생 테스트
*/
@Test
void testRequestChatbot_CommunicationError() {
// Given
mockWebServer.enqueue(new MockResponse().setResponseCode(500));

ClovaRequestDto request = new ClovaRequestDto(null, 0.8, 0.3, 256, 1.2);

// When & Then
StepVerifier.create(clovaApiClient.requestChatbot(request))
.expectErrorMatches(throwable -> throwable instanceof CustomException
&& ((CustomException) throwable).getErrorCode() == ChatbotErrorStatus._CHATBOT_API_COMMUNICATION_ERROR)
.verify();
}

/**
* 요청 본문 확인 테스트
*/
@Test
void testRequestBodyMapping() throws InterruptedException {
// Given
mockWebServer.enqueue(new MockResponse()
.setResponseCode(200)
.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.setBody("""
{
"result": {
"message": {
"content": "응답 메시지"
}
}
}
"""));

ClovaRequestDto request = new ClovaRequestDto(null, 0.8, 0.3, 256, 1.2);

// When
Mono<String> response = clovaApiClient.requestChatbot(request);

// Then
StepVerifier.create(response)
.expectNext("응답 메시지")
.verifyComplete();

// 요청 확인
String recordedRequest = mockWebServer.takeRequest().getBody().readUtf8();
assertNotNull(recordedRequest);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package kusitms.backend.chatbot;
package kusitms.backend.chatbot.presentation;

import com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper;
import com.epages.restdocs.apispec.ResourceSnippetParameters;
import com.epages.restdocs.apispec.Schema;
import kusitms.backend.chatbot.application.dto.response.GetClovaChatbotAnswerResponseDto;
import kusitms.backend.chatbot.application.dto.response.GetGuideChatbotAnswerResponseDto;
import kusitms.backend.chatbot.application.service.ChatbotApplicationService;
import kusitms.backend.chatbot.presentation.ChatbotController;
import kusitms.backend.configuration.ControllerTestConfig;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
Expand Down

0 comments on commit 1ec0a20

Please sign in to comment.