Skip to content

Commit

Permalink
Fix: ScaleControllerTest에서 WebMvcTest를 사용하도록 변경 (#313)
Browse files Browse the repository at this point in the history
* Chore: 스프링 security 의존성 추가

* Fix: ScaleControllerTest를 mock mvc 테스트로 변경
  • Loading branch information
Jaewon-pro authored Nov 20, 2024
1 parent f29f92a commit e26dae1
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 71 deletions.
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ spring-boot-starter-actuator = { group = "org.springframework.boot", name = "spr
spring-boot-starter-data-jpa = { group = "org.springframework.boot", name = "spring-boot-starter-data-jpa", version.ref = "spring-boot" }
spring-boot-devtools = { group = "org.springframework.boot", name = "spring-boot-devtools", version.ref = "spring-boot" }
spring-boot-starter-test = { group = "org.springframework.boot", name = "spring-boot-starter-test", version.ref = "spring-boot" }
spring-boot-security-test = { group = "org.springframework.security", name = "spring-security-test", version = "6.3.4" }
spring-boot-testcontainers = { group = "org.springframework.boot", name = "spring-boot-testcontainers", version.ref = "spring-boot" }
spring-boot-cache = { group = "org.springframework.boot", name = "spring-boot-starter-cache", version.ref = "spring-boot" }
spring-boot-aop = { group = "org.springframework.boot", name = "spring-boot-starter-aop", version.ref = "spring-boot" }
Expand Down Expand Up @@ -66,6 +67,7 @@ spring-boot = [
spring-boot-test = [
"spring-boot-devtools",
"spring-boot-starter-test",
"spring-boot-security-test",
"spring-cloud-starter-contract-stub-runner",
]

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.dnd.runus.presentation.config;

import com.dnd.runus.auth.token.access.AccessTokenProvider;
import com.dnd.runus.presentation.filter.AuthenticationCheckFilter;
import com.dnd.runus.presentation.handler.ApiResponseHandler;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.validation.constraints.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

/**
* 컨트롤러 테스트를 위한 Helper 클래스입니다.
* <p>MockMvc를 초기화하고, ObjectMapper를 주입합니다.
*/
@Import({ObjectMapperConfig.class, ApiResponseHandler.class})
public abstract class ControllerTestHelper {
@Autowired
private ApiResponseHandler apiResponseHandler;

protected MockMvc mvc = null;

@Autowired
protected ObjectMapper objectMapper;

@MockBean
protected AccessTokenProvider accessTokenProvider;

@MockBean
protected AuthenticationCheckFilter authenticationCheckFilter;

/**
* MockMvc를 초기화합니다.
* <p>테스트를 진행하기 전에 반드시 호출해야 합니다.
*
* @param controller MockMvc를 초기화할 컨트롤러
*/
protected void setUpMockMvc(@NotNull Object controller) {
mvc = MockMvcBuilders.standaloneSetup(controller)
.setControllerAdvice(apiResponseHandler)
.setMessageConverters(new MappingJackson2HttpMessageConverter(objectMapper))
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,31 @@
import com.dnd.runus.application.scale.dto.CoursesDto;
import com.dnd.runus.domain.scale.Scale;
import com.dnd.runus.domain.scale.ScaleAchievementLog;
import com.dnd.runus.presentation.v1.scale.dto.ScaleCoursesResponse;
import com.dnd.runus.presentation.v1.scale.dto.ScaleCoursesResponse.CurrentCourse;
import com.dnd.runus.presentation.config.ControllerTestHelper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
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.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.ResultActions;

import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

@ExtendWith(MockitoExtension.class)
class ScaleControllerTest {
@WithMockUser
@WebMvcTest(ScaleController.class)
class ScaleControllerTest extends ControllerTestHelper {

@InjectMocks
@Autowired
private ScaleController scaleController;

@Mock
@MockBean
private ScaleService scaleService;

private long memberId;
Expand All @@ -38,6 +39,8 @@ class ScaleControllerTest {

@BeforeEach
void setUp() {
setUpMockMvc(scaleController);

memberId = 1;
scale1 = new Scale(1, "서울에서 인천", 200, 1, "서울", "인천");
scale2 = new Scale(2, "인천에서 대전", 1000, 2, "인천", "대전");
Expand All @@ -47,50 +50,41 @@ void setUp() {

@DisplayName("반환 문구, km 단위 등을 확인합니다.")
@Test
void getAchievements() {
void getAchievements() throws Exception {
// given
given(scaleService.getAchievements(memberId))
.willReturn(CoursesDto.builder()
.totalCoursesCount(4)
.totalCoursesDistanceMeter(1_004_200)
.myTotalRunningMeter(450)
.achievedCourses(List.of(CoursesDto.Course.of(
new ScaleAchievementLog(scale1, OffsetDateTime.now()), scale1.sizeMeter(), 200)))
.currentCourse(CoursesDto.Course.of(
new ScaleAchievementLog(scale2, null), scale1.sizeMeter() + scale2.sizeMeter(), 250))
.build());
OffsetDateTime time =
OffsetDateTime.of(2021, 1, 1, 10, 0, 0, 0, OffsetDateTime.now().getOffset());

CoursesDto coursesDto = CoursesDto.builder()
.totalCoursesCount(4)
.totalCoursesDistanceMeter(1_004_200)
.myTotalRunningMeter(450)
.achievedCourses(
List.of(CoursesDto.Course.of(new ScaleAchievementLog(scale1, time), scale1.sizeMeter(), 200)))
.currentCourse(CoursesDto.Course.of(
new ScaleAchievementLog(scale2, null), scale1.sizeMeter() + scale2.sizeMeter(), 250))
.build();

given(scaleService.getAchievements(memberId)).willReturn(coursesDto);

// when
ScaleCoursesResponse response = scaleController.getCourses(memberId);

// 1. info 검증
ScaleCoursesResponse.Info info = response.info();
assertNotNull(info);
assertEquals(4, info.totalCourses());
// #,###.#km 포멧 확인
assertEquals("1,004.2km", info.totalDistance());

// 2. current Course 확인
ScaleCoursesResponse.CurrentCourse currentCourse = response.currentCourse();
assertNotNull(currentCourse);
assertEquals(scale2.name(), currentCourse.name());
assertEquals("1km", currentCourse.totalDistance());
assertEquals("250m", currentCourse.achievedDistance());
// 남은 거리가 1km미만일 경우, 소숫점 둘째자리에서 반올림
assertEquals("대전까지 0.8km 남았어요!", currentCourse.message());

// 3. achieved 확인
List<ScaleCoursesResponse.AchievedCourse> achievedCourses = response.achievedCourses();
assertFalse(achievedCourses.isEmpty());
ScaleCoursesResponse.AchievedCourse achievedCourse = achievedCourses.getFirst();
assertEquals(scale1.name(), achievedCourse.name());
assertEquals("200m", achievedCourse.totalDistance());
assertEquals(LocalDate.now(), achievedCourse.achievedAt());
ResultActions result = mvc.perform(get("/api/v1/scale/courses").param("memberId", String.valueOf(memberId)));

// then
result.andExpect(jsonPath("$.data.info.totalCourses").value(4))
.andExpect(jsonPath("$.data.info.totalDistance").value("1,004.2km"))
.andExpect(jsonPath("$.data.currentCourse.name").value(scale2.name()))
.andExpect(jsonPath("$.data.currentCourse.totalDistance").value("1km"))
.andExpect(jsonPath("$.data.currentCourse.achievedDistance").value("250m"))
.andExpect(jsonPath("$.data.currentCourse.message").value("대전까지 0.8km 남았어요!"))
.andExpect(jsonPath("$.data.achievedCourses[0].name").value(scale1.name()))
.andExpect(jsonPath("$.data.achievedCourses[0].totalDistance").value("200m"))
.andExpect(jsonPath("$.data.achievedCourses[0].achievedAt").value("2021-01-01"));
}

@DisplayName("달성한 코스가 없는 경우, 성취 코스는 빈 리스트를 반환합니다.")
@Test
void getAchievements_without_achieved() {
void getAchievements_without_achieved() throws Exception {
// given
given(scaleService.getAchievements(memberId))
.willReturn(CoursesDto.builder()
Expand All @@ -102,23 +96,19 @@ void getAchievements_without_achieved() {
.build());

// when
ScaleCoursesResponse response = scaleController.getCourses(memberId);

// 1. achieved는 빈 리스트
assertTrue(response.achievedCourses().isEmpty());

// 2. current Course 확인
ScaleCoursesResponse.CurrentCourse currentCourse = response.currentCourse();
assertNotNull(currentCourse);
assertEquals(scale1.name(), currentCourse.name());
assertEquals("200m", currentCourse.totalDistance());
assertEquals("50m", currentCourse.achievedDistance());
assertEquals(scale1.endName() + "까지 0.2km 남았어요!", currentCourse.message());
ResultActions result = mvc.perform(get("/api/v1/scale/courses").param("memberId", String.valueOf(memberId)));

// then
result.andExpect(jsonPath("$.data.achievedCourses").isEmpty())
.andExpect(jsonPath("$.data.currentCourse.name").value(scale1.name()))
.andExpect(jsonPath("$.data.currentCourse.totalDistance").value("200m"))
.andExpect(jsonPath("$.data.currentCourse.achievedDistance").value("50m"))
.andExpect(jsonPath("$.data.currentCourse.message").value("인천까지 0.2km 남았어요!"));
}

@DisplayName("전체 코스를 달성했을 경우, 현재 코스는 지구한바퀴로 리턴합니다.")
@Test
void getAchievements_all_achieved() {
void getAchievements_all_achieved() throws Exception {
// given
given(scaleService.getAchievements(memberId))
.willReturn(CoursesDto.builder()
Expand Down Expand Up @@ -151,14 +141,13 @@ void getAchievements_all_achieved() {
.build());

// when
ScaleCoursesResponse response = scaleController.getCourses(memberId);
assertEquals(4, response.achievedCourses().size());

CurrentCourse currentCourse = response.currentCourse();
assertNotNull(currentCourse);
assertEquals("지구 한바퀴", currentCourse.name());
assertEquals("1,004.2km", currentCourse.totalDistance());
assertEquals("1,004.5km", currentCourse.achievedDistance());
assertEquals("축하합니다! 지구 한바퀴 완주하셨네요!", currentCourse.message());
ResultActions result = mvc.perform(get("/api/v1/scale/courses").param("memberId", String.valueOf(memberId)));

// then
result.andExpect(jsonPath("$.data.achievedCourses.length()").value(4))
.andExpect(jsonPath("$.data.currentCourse.name").value("지구 한바퀴"))
.andExpect(jsonPath("$.data.currentCourse.totalDistance").value("1,004.2km"))
.andExpect(jsonPath("$.data.currentCourse.achievedDistance").value("1,004.5km"))
.andExpect(jsonPath("$.data.currentCourse.message").value("축하합니다! 지구 한바퀴 완주하셨네요!"));
}
}

0 comments on commit e26dae1

Please sign in to comment.