From f86405765395487240d327e2ba5de947f7f8e5ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=B0=AC?= Date: Tue, 13 Feb 2024 17:06:44 +0900 Subject: [PATCH 1/5] =?UTF-8?q?[chore]=20EnableJpaAuditing=20=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=9D=B4=EB=8F=99=20[?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=9E=90=EB=A3=8C](https://velog.io/@twoo?= =?UTF-8?q?ne14/Spring-Junit-WebMvcTest-%EC%9D%98-JPA-metamodel-must-not-b?= =?UTF-8?q?e-empty-%EC%97%90%EB%9F%AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/qp/official/qp/QpBackendApplication.java | 1 - .../qp/official/qp/config/JpaEnversConfiguration.java | 9 +++++++++ src/main/java/qp/official/qp/domain/Question.java | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 src/main/java/qp/official/qp/config/JpaEnversConfiguration.java diff --git a/src/main/java/qp/official/qp/QpBackendApplication.java b/src/main/java/qp/official/qp/QpBackendApplication.java index ca40cdd5..c53327c3 100644 --- a/src/main/java/qp/official/qp/QpBackendApplication.java +++ b/src/main/java/qp/official/qp/QpBackendApplication.java @@ -8,7 +8,6 @@ import java.util.TimeZone; @SpringBootApplication -@EnableJpaAuditing public class QpBackendApplication { static { diff --git a/src/main/java/qp/official/qp/config/JpaEnversConfiguration.java b/src/main/java/qp/official/qp/config/JpaEnversConfiguration.java new file mode 100644 index 00000000..54bc5142 --- /dev/null +++ b/src/main/java/qp/official/qp/config/JpaEnversConfiguration.java @@ -0,0 +1,9 @@ +package qp.official.qp.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@Configuration +@EnableJpaAuditing +public class JpaEnversConfiguration { +} diff --git a/src/main/java/qp/official/qp/domain/Question.java b/src/main/java/qp/official/qp/domain/Question.java index ffc3ce3c..91b38aad 100644 --- a/src/main/java/qp/official/qp/domain/Question.java +++ b/src/main/java/qp/official/qp/domain/Question.java @@ -8,6 +8,7 @@ import qp.official.qp.web.dto.QuestionRequestDTO; import javax.persistence.*; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; From 3f104aaeac2dab09749118c0e7d432a8ff186c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=B0=AC?= Date: Tue, 13 Feb 2024 17:07:10 +0900 Subject: [PATCH 2/5] =?UTF-8?q?[chore]=20ObjectMapper=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=ED=95=9C=EA=B8=80=20=EC=84=A4=EC=A0=95=20[=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=9E=90=EB=A3=8C](https://www.notion.so/Gson-c606?= =?UTF-8?q?b278aa414d68b9ff97ef85860a7f=3Fpvs=3D4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../qp/config/ObjectMapperConfig.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/qp/official/qp/config/ObjectMapperConfig.java diff --git a/src/main/java/qp/official/qp/config/ObjectMapperConfig.java b/src/main/java/qp/official/qp/config/ObjectMapperConfig.java new file mode 100644 index 00000000..9bdbbce7 --- /dev/null +++ b/src/main/java/qp/official/qp/config/ObjectMapperConfig.java @@ -0,0 +1,21 @@ +package qp.official.qp.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +import static java.nio.charset.StandardCharsets.UTF_8; + +@Configuration +public class ObjectMapperConfig implements WebMvcConfigurer { + @Override + public void configureMessageConverters(List> converters) { + converters.stream() + .filter(converter -> converter instanceof MappingJackson2HttpMessageConverter) + .findFirst() + .ifPresent(converter -> ((MappingJackson2HttpMessageConverter) converter).setDefaultCharset(UTF_8)); + } +} From d77356005e65ee8c11517b834145720995144014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=B0=AC?= Date: Tue, 13 Feb 2024 17:08:40 +0900 Subject: [PATCH 3/5] =?UTF-8?q?[chore]=20QuestionController=EC=97=90=20tok?= =?UTF-8?q?enService=20=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=EC=8B=9C=20tokenService=EB=A5=BC=20MockBean=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=EB=A1=9C=20=EC=A3=BC=EC=9E=85=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/qp/official/qp/web/controller/QuestionController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/qp/official/qp/web/controller/QuestionController.java b/src/main/java/qp/official/qp/web/controller/QuestionController.java index a40f5244..59970d18 100644 --- a/src/main/java/qp/official/qp/web/controller/QuestionController.java +++ b/src/main/java/qp/official/qp/web/controller/QuestionController.java @@ -32,6 +32,7 @@ public class QuestionController { private final QuestionCommandService questionCommandService; private final QuestionQueryService questionQueryService; + private final TokenService tokenService; // 질문 작성 @PostMapping From 8a713af4438d433bb344e3566163c2dfcf8672b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=B0=AC?= Date: Tue, 13 Feb 2024 17:17:50 +0900 Subject: [PATCH 4/5] =?UTF-8?q?[test]=20QuestionControllerTest.createQuest?= =?UTF-8?q?ion=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/QuestionControllerTest.java | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 src/test/java/qp/official/qp/web/controller/QuestionControllerTest.java diff --git a/src/test/java/qp/official/qp/web/controller/QuestionControllerTest.java b/src/test/java/qp/official/qp/web/controller/QuestionControllerTest.java new file mode 100644 index 00000000..f04eceea --- /dev/null +++ b/src/test/java/qp/official/qp/web/controller/QuestionControllerTest.java @@ -0,0 +1,132 @@ +package qp.official.qp.web.controller; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import qp.official.qp.apiPayload.ApiResponse; +import qp.official.qp.domain.Hashtag; +import qp.official.qp.domain.Question; +import qp.official.qp.domain.User; +import qp.official.qp.repository.HashtagRepository; +import qp.official.qp.repository.UserRepository; +import qp.official.qp.service.QuestionService.QuestionCommandService; +import qp.official.qp.service.QuestionService.QuestionQueryService; +import qp.official.qp.service.TokenService.TokenService; +import qp.official.qp.web.dto.QuestionRequestDTO; +import qp.official.qp.web.dto.QuestionResponseDTO; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +// WebMvcTest를 사용하여 QuestionController를 테스트 +// WebMvcTest는 Controller가 의존하는 Bean들만 로드하여 테스트 +@WebMvcTest(QuestionController.class) +class QuestionControllerTest { + + @MockBean + private QuestionCommandService questionCommandService; + @MockBean + private QuestionQueryService questionQueryService; + + // ExistHashTag 어노테이션에서 사용하는 HashtagRepository + @MockBean + private HashtagRepository hashtagRepository; + + // ExistUser 어노테이션에서 사용하는 UserRepository + @MockBean + private UserRepository userRepository; + + @MockBean + private TokenService tokenService; + + // MockMvc를 사용하여 API를 테스트 + private final MockMvc mockMvc; + + // ObjectMapper를 사용하여 객체를 JSON으로 변환 또는 JSON을 객체로 변환 + private final ObjectMapper objectMapper; + + @Autowired + public QuestionControllerTest( + MockMvc mockMvc, + ObjectMapper objectMapper + ) { + this.mockMvc = mockMvc; + this.objectMapper = objectMapper; + } + + @Test + void createQuestion() throws Exception { + // given + String testTitle = "new title"; + String testContent = "new content"; + + Long userId = 1L; + Long questionId = 1L; + LocalDateTime createdAt = LocalDateTime.now(); + + // Request 객체 생성 + QuestionRequestDTO.CreateDTO questionRequest = QuestionRequestDTO.CreateDTO.builder() + .userId(userId) + .title(testTitle) + .content(testContent) + .hashtag(new ArrayList<>()) + .build(); + + + // Expect Response 객체 생성 + Question questionResponse = Question.builder() + .questionId(questionId) + .title(testTitle) + .content(testContent) + .answers(new ArrayList<>()) + .build(); + + // Setter 없이 ReflectionTestUtils를 사용하여 필드값을 설정 + ReflectionTestUtils.setField(questionResponse, "createdAt", createdAt); + + // questionCommandService 에서 createQuestion 메소드가 호출될 때 questionResponse를 반환하도록 설정 + when(questionCommandService.createQuestion(any(QuestionRequestDTO.CreateDTO.class))).thenReturn(questionResponse); + // ExistUser 어노테이션을 통과하기 위해 userRepository 에서 findById 메소드가 호출될 때 아무 Optional.of(User.builder().build())를 반환하도록 설정 + when(userRepository.findById(any(Long.class))).thenReturn(Optional.of(User.builder().build())); + // ExistHashTag 어노테이션을 통과하기 위해 hashtagRepository 에서 findById 메소드가 호출될 때 아무 Optional.of(Hashtag.builder().build())를 반환하도록 설정 + when(hashtagRepository.findById(any(Long.class))).thenReturn(Optional.of(Hashtag.builder().build())); + + // when + // Request 객체를 JSON으로(request body로) 변환 + String body = objectMapper.writeValueAsString(questionRequest); + // API 호출 + ResultActions action = mockMvc.perform(MockMvcRequestBuilders.post("/questions") + .contentType("application/json") + .content(body)); + + // then + // API 호출 결과가 200 OK인지 확인 + action.andExpect(status().isOk()); + + // API 호출 결과를 ApiResponse 객체로 변환 + ApiResponse response = objectMapper.readValue( + action.andReturn().getResponse().getContentAsString(), + new TypeReference<>() { + } + ); + + // questionId 확인 + assertEquals(questionId, response.getResult().getQuestionId()); + + // createAt 확인 + assertEquals(createdAt, response.getResult().getCreatedAt()); + } +} \ No newline at end of file From a6455d60f985f29a856be7146fff3796e9b23e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=B0=AC?= Date: Tue, 13 Feb 2024 17:18:49 +0900 Subject: [PATCH 5/5] =?UTF-8?q?[chore]=20Application=20Class=20=EC=9D=98?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20imp?= =?UTF-8?q?ort=EB=AC=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/qp/official/qp/QpBackendApplication.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/qp/official/qp/QpBackendApplication.java b/src/main/java/qp/official/qp/QpBackendApplication.java index c53327c3..8ba783aa 100644 --- a/src/main/java/qp/official/qp/QpBackendApplication.java +++ b/src/main/java/qp/official/qp/QpBackendApplication.java @@ -2,7 +2,6 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; - import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import javax.annotation.PostConstruct; import java.util.TimeZone;