Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test: 사용자 기본 프로필 정보 조회 테스트 코드 작성 #327

Merged
merged 8 commits into from
Feb 16, 2025
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation "org.springframework.restdocs:spring-restdocs-mockmvc:3.0.0"

// Lombok
compileOnly 'org.projectlombok:lombok'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public ApiResponse<MemberResponseDTO.MemberImageDTO> getProfileImage(@PathVariab
@Operation(summary = "사용자 프로필 기본 정보 조회", description = "마이페이지 사용자 정보 조회 기능")
public ApiResponse<MemberResponseDTO.SimpleMemberDTO> getUserProfile(@AuthenticationPrincipal CustomMemberDetails memberDetails, @PathVariable(value = "member_id") Long memberId){
Member currentMember = memberQueryService.getUserInfo(memberDetails.getId());
Member user = memberQueryService.getUserProfile(memberId);
Member user = memberQueryService.getUserInfo(memberId);
return ApiResponse.onSuccess(SuccessStatus.MEMBER_OK, MemberConverter.toSimpleMemberResponseDto(currentMember, user));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,8 @@ public void setImage(MemberImage image) {
public void setTeamMemberList(List<TeamMember> teamMembers) {
this.teamMemberList = teamMembers;
}

public void setMemberId(Long id) { this.memberId = id; }

public void setEmail(String mail) { this.email = mail; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,6 @@ public ApiResponse<MemberResponseDTO.MemberImageDTO> getProfileImage(Long member
return ApiResponse.onSuccess(SuccessStatus.MEMBER_OK, memberRepository.findProfileImageUrl(memberId));
}

public Member getUserProfile(Long memberId) {
Member user = memberRepository.findMemberWithTechStacksAndProjectsAndTeam(memberId)
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));
return user;
}

public Member getUserInfo(Long memberId) {
Member user = memberRepository.findMemberWithTechStacksAndProjectsAndTeam(memberId)
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import lombok.Getter;

@Getter
@AllArgsConstructor
public class GeneralException extends RuntimeException {

private BaseErrorCode code;
Expand All @@ -16,5 +15,9 @@ public ErrorReasonDTO getErrorReason() {
public ErrorReasonDTO getErrorReasonHttpStatus(){
return this.code.getReasonHttpStatus();
}
public GeneralException(BaseErrorCode code) {
super(code.getReason().getMessage());
this.code = code;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.codiary.backend.domain.member.controller;

import com.codiary.backend.domain.member.entity.Member;
import com.codiary.backend.domain.member.util.MemberUtilTest;
import com.codiary.backend.global.apiPayload.code.status.ErrorStatus;
import com.codiary.backend.global.util.ControllerTest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.restdocs.RestDocumentationExtension;

import java.util.Optional;

import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

@SpringBootTest
@ExtendWith({MockitoExtension.class, RestDocumentationExtension.class})
@AutoConfigureMockMvc
@DisplayName("MemberController 테스트")
public class MemberControllerTest extends ControllerTest {
private Member member2;

@BeforeEach
void setUp() {
member2 = MemberUtilTest.createMember2();
member2.setMemberId(2L);
}

@Nested
@DisplayName("사용자 기본 프로필 조회 메서드 테스트")
class GetUserProfileTest {

@Test
@DisplayName("✅ member_id가 주어지면 해당하는 사용자 정보를 조회한다.")
void getUserProfile_ShouldReturnUserProfile() throws Exception {
// given: 조회하는 사용자 정보와 accessToken, 조회 대상 사용자의 정보 Mocking
String accessToken = jwtTokenProvider.generateToken(member1.getEmail()).getAccessToken();
given(memberRepository.findMemberWithTechStacksAndProjectsAndTeam(member1.getMemberId()))
.willReturn(Optional.of(member1));
given(memberRepository.findMemberWithTechStacksAndProjectsAndTeam(member2.getMemberId()))
.willReturn(Optional.of(member2));

// when & then: 사용자 정보 조회 API 호출 시 조회 대상 사용자의 정보 반환
mockMvc.perform(get("/api/v2/member/profile/{member_id}", member2.getMemberId())
.header("Authorization", "Bearer " + accessToken))
.andDo(print())
.andExpect(status().isOk()) // HTTP status 200 OK 응답 검증
.andExpect(jsonPath("$.isSuccess").value(true)) // isSuccess 값 검증
.andExpect(jsonPath("$.code").value("MEMBER_1000")) // code 값 검증
.andExpect(jsonPath("$.message").value("성공입니다.")) // message 값 검증
.andExpect(jsonPath("$.result.current_member_id").value(member1.getMemberId())) // current_member_id 값 검증
.andExpect(jsonPath("$.result.user_id").value(member2.getMemberId())) // user_id 값 검증
.andExpect(jsonPath("$.result.user_name").value(member2.getNickname())); // user_name 값 검증
}

@Test
@DisplayName("❌ 존재하지 않는 사용자 프로필 조회 시 예외가 발생한다.")
void getUserProfile_ShouldReturnError_WhenUserNotFound() throws Exception {
// given: 존재하지 않는 사용자 ID를 조회하는 경우
Long nonExistentMemberId = 999L;
String accessToken = jwtTokenProvider.generateToken(member1.getEmail()).getAccessToken();
given(memberRepository.findMemberWithTechStacksAndProjectsAndTeam(nonExistentMemberId))
.willReturn(Optional.empty());

// when & then: 존재하지 않는 사용자 ID로 사용자 정보 조회 시 오류 발생
mockMvc.perform(get("/api/v2/member/profile/{member_id}", nonExistentMemberId)
.header("Authorization", "Bearer " + accessToken))
.andDo(print())
.andExpect(status().isBadRequest()) // HTTP status 400 Bad Request
.andExpect(jsonPath("$.isSuccess").value(false)) // 실패 여부 확인
.andExpect(jsonPath("$.code").value(ErrorStatus.MEMBER_NOT_FOUND.getCode())) // MEMBER_1001 코드 확인
.andExpect(jsonPath("$.message").value(ErrorStatus.MEMBER_NOT_FOUND.getMessage())); // 실패 메시지 확인
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.codiary.backend.domain;
package com.codiary.backend.domain.member.entity;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.codiary.backend.domain.member.service;

import com.codiary.backend.domain.member.entity.Member;
import com.codiary.backend.domain.member.repository.MemberRepository;
import com.codiary.backend.domain.member.util.MemberUtilTest;
import com.codiary.backend.global.apiPayload.code.status.ErrorStatus;
import com.codiary.backend.global.apiPayload.exception.GeneralException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
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 java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.*;

@DisplayName("MemberQueryService 유닛테스트")
@ExtendWith(MockitoExtension.class)
public class MemberQueryServiceTest {
@InjectMocks
private MemberQueryService memberQueryService;

@Mock
private MemberRepository memberRepository;

private final Member member1 = MemberUtilTest.createMember1();
private final Member member2 = MemberUtilTest.createMember2();

@Nested
@DisplayName("사용자 기본 정보 조회 메서드 테스트")
class GetUserInfoTest {

@Test
@DisplayName("✅ memberId가 주어지면 해당 사용자를 반환한다.")
void shouldReturnMemberWhenValidIdGiven() {
// given
Long memberId = member1.getMemberId();
given(memberRepository.findMemberWithTechStacksAndProjectsAndTeam(memberId))
.willReturn(Optional.of(member1)); // Member1 반환하도록 Mocking

// when
Member result = memberQueryService.getUserInfo(memberId);

// then
assertThat(result).isNotNull();
assertThat(result.getMemberId()).isEqualTo(memberId); // 반환된 Member의 memberId가 주어진 memberId와 같은지 확인
verify(memberRepository, times(1))
.findMemberWithTechStacksAndProjectsAndTeam(memberId); // memberRepository의 메서드가 1번 호출되었는지 확인
}

@Test
@DisplayName("❌ 존재하지 않는 memberId를 조회하면 예외가 발생한다.")
void shouldThrowExceptionWhenMemberNotFound() {
// given
Long invalidMemberId = 999L;
given(memberRepository.findMemberWithTechStacksAndProjectsAndTeam(invalidMemberId))
.willReturn(Optional.empty()); // 빈 Optional 반환하도록 Mocking

// when & then
assertThatThrownBy(() -> memberQueryService.getUserInfo(invalidMemberId)) // 예외 발생 여부 확인
.isInstanceOf(GeneralException.class)
.hasMessageContaining(ErrorStatus.MEMBER_NOT_FOUND.getMessage());

verify(memberRepository, times(1))
.findMemberWithTechStacksAndProjectsAndTeam(invalidMemberId); // memberRepository의 메서드가 1번 호출되었는지 확인
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.codiary.backend.domain.member.util;

import com.codiary.backend.domain.member.entity.Member;

public class MemberUtilTest {
public static Member createMember1() {
return Member.builder()
.email("[email protected]")
.nickname("codiary1")
.password("codiary1234")
.birth("1996-01-01")
.discord("codiary#1234")
.github("codiary#1234")
.linkedin("codiary#1234")
.build();
}

public static Member createMember2() {
return Member.builder()
.email("[email protected]")
.nickname("codiary2")
.password("codiary1234")
.birth("1996-01-01")
.discord("codiary#1234")
.github("codiary#1234")
.linkedin("codiary#1234")
.build();
}
}
83 changes: 83 additions & 0 deletions src/test/java/com/codiary/backend/global/util/ControllerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.codiary.backend.global.util;

import com.codiary.backend.domain.member.entity.Member;
import com.codiary.backend.domain.member.repository.MemberRepository;
import com.codiary.backend.domain.member.security.CustomMemberDetails;
import com.codiary.backend.domain.member.security.CustomMemberDetailsService;
import com.codiary.backend.domain.member.service.MemberQueryService;
import com.codiary.backend.domain.member.util.MemberUtilTest;
import com.codiary.backend.global.jwt.JwtTokenProvider;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.BDDMockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.restdocs.RestDocumentationContextProvider;
import org.springframework.restdocs.RestDocumentationExtension;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;

import java.util.Optional;

import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;

@ExtendWith({MockitoExtension.class, RestDocumentationExtension.class})
@AutoConfigureMockMvc
public abstract class ControllerTest {
@Autowired
protected MockMvc mockMvc;

@Autowired
WebApplicationContext wac;

@Autowired
public MemberQueryService memberQueryService;

@Autowired
public JwtTokenProvider jwtTokenProvider;

@MockBean
protected MemberRepository memberRepository;

@MockBean
protected CustomMemberDetailsService customMemberDetailsService;

public Member member1;

@BeforeEach
void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.addFilter(new CharacterEncodingFilter("UTF-8", true))
.apply(documentationConfiguration(restDocumentation))
.build();

// member1 생성 및 설정
member1 = MemberUtilTest.createMember1();
member1.setMemberId(1L);

// `MemberRepository` 모킹하여 member1의 이메일에 해당하는 사용자를 반환하도록 설정
given(memberRepository.findByEmail(member1.getEmail())).willReturn(Optional.of(member1));

// `CustomMemberDetails` 생성 및 모킹
CustomMemberDetails customMemberDetails = new CustomMemberDetails(
member1.getEmail(), member1.getMemberId(), member1.getPassword(), null);
given(customMemberDetailsService.loadUserByUsername(anyString()))
.willReturn(customMemberDetails);

// SecurityContext에 CustomMemberDetails 설정
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken(
customMemberDetails, null, customMemberDetails.getAuthorities()));
SecurityContextHolder.setContext(securityContext);
}
}
Loading