1️⃣ UserServiceTest
회원가입, 로그인, 토큰 재발급, 로그아웃 등 로직 검증
더보기
class UserServiceTest {
private UserService userService;
private UserRepository userRepository;
private PasswordEncoder passwordEncoder;
private JwtTokenProvider jwtTokenProvider;
private RefreshTokenService refreshTokenService;
@BeforeEach
void setUp() {
userRepository = mock(UserRepository.class);
passwordEncoder = mock(PasswordEncoder.class);
jwtTokenProvider = mock(JwtTokenProvider.class);
refreshTokenService = mock(RefreshTokenService.class);
userService = new UserService(userRepository, passwordEncoder, jwtTokenProvider, refreshTokenService);
}
@Test
void signup_정상동작() {
SignupRequestDto dto = new SignupRequestDto();
ReflectionTestUtils.setField(dto, "email", "user@email.com");
ReflectionTestUtils.setField(dto, "password", "Password123!");
ReflectionTestUtils.setField(dto, "nickname", "닉네임");
ReflectionTestUtils.setField(dto, "phone", "01012345678");
ReflectionTestUtils.setField(dto, "role", Role.ROLE_USER);
ReflectionTestUtils.setField(dto, "socialType", SocialType.NORMAL);
when(passwordEncoder.encode(dto.getPassword())).thenReturn("encodedPass");
userService.signup(dto);
verify(userRepository).checkEmailDuplicate(dto.getEmail());
verify(userRepository).checkNicknameDuplicate(dto.getNickname());
verify(userRepository).save(any(User.class));
}
@Test
void login_성공시_토큰반환() {
LoginRequestDto dto = new LoginRequestDto();
ReflectionTestUtils.setField(dto, "email", "user@email.com");
ReflectionTestUtils.setField(dto, "password", "Password123!");
User user = User.builder()
.id(1L)
.email(dto.getEmail())
.password("encoded")
.nickname("닉네임")
.role(Role.ROLE_USER)
.status(UserStatus.ACTIVE)
.socialType(SocialType.NORMAL)
.build();
when(userRepository.findByEmailOrThrow(dto.getEmail())).thenReturn(user);
when(passwordEncoder.matches(dto.getPassword(), user.getPassword())).thenReturn(true);
when(jwtTokenProvider.generateTokenPair(any(), any(), any(), any()))
.thenReturn(new TokenResponseDto("access-token", "refresh-token"));
when(jwtTokenProvider.getRefreshTokenDuration()).thenReturn(Duration.ofDays(7));
TokenResponseDto token = userService.login(dto);
assertEquals("access-token", token.getAccessToken());
assertEquals("refresh-token", token.getRefreshToken());
verify(refreshTokenService).save(eq(user.getId()), eq("refresh-token"), any());
}
@Test
void reissue_성공시_새로운_토큰발급() {
ReissueRequestDto dto = new ReissueRequestDto();
ReflectionTestUtils.setField(dto, "refreshToken", "oldRefreshToken");
when(jwtTokenProvider.getAuthorId("oldRefreshToken")).thenReturn(1L);
when(jwtTokenProvider.getEmail("oldRefreshToken")).thenReturn("user@email.com");
when(jwtTokenProvider.getNickname("oldRefreshToken")).thenReturn("닉네임");
when(jwtTokenProvider.getRole("oldRefreshToken")).thenReturn(Role.ROLE_USER);
when(refreshTokenService.get(1L)).thenReturn("oldRefreshToken");
when(jwtTokenProvider.generateTokenPair(any(), any(), any(), any()))
.thenReturn(new TokenResponseDto("newAccess", "newRefresh"));
when(jwtTokenProvider.getRefreshTokenDuration()).thenReturn(Duration.ofDays(7));
TokenResponseDto result = userService.reissue(dto);
assertEquals("newAccess", result.getAccessToken());
assertEquals("newRefresh", result.getRefreshToken());
verify(refreshTokenService).save(eq(1L), eq("newRefresh"), any());
}
@Test
void logout_성공시_토큰삭제() {
when(refreshTokenService.exists(1L)).thenReturn(true);
userService.logout(1L);
verify(refreshTokenService).delete(1L);
}
@Test
void logout_토큰없을시_예외() {
when(refreshTokenService.exists(1L)).thenReturn(false);
assertThrows(UserException.class, () -> userService.logout(1L));
}
}
2️⃣ UserControllerTest
단건 조회, 회원수정, 비밀번호 변경 등 컨트롤러 기능 정상 응답 여부
더보기
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private UserService userService;
@MockBean
private JwtTokenProvider jwtTokenProvider;
@Test
@DisplayName("회원가입 요청 성공")
void signup() throws Exception {
SignupRequestDto request = new SignupRequestDto();
// 필드 설정
setField(request, "email", "test@email.com");
setField(request, "password", "Password123!");
setField(request, "nickname", "닉네임");
setField(request, "phone", "01012345678");
setField(request, "role", Role.ROLE_USER);
setField(request, "socialType", SocialType.NORMAL);
mockMvc.perform(post("/api/v1/users/signup")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isOk());
verify(userService).signup(any(SignupRequestDto.class));
}
@Test
@DisplayName("로그인 성공")
void login() throws Exception {
LoginRequestDto request = new LoginRequestDto();
setField(request, "email", "user@email.com");
setField(request, "password", "Password123!");
when(userService.login(any())).thenReturn(new TokenResponseDto("access", "refresh"));
mockMvc.perform(post("/api/v1/users/signin")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.accessToken").value("access"))
.andExpect(jsonPath("$.refreshToken").value("refresh"));
}
@Test
@DisplayName("토큰 재발급 성공")
void reissue() throws Exception {
ReissueRequestDto request = new ReissueRequestDto();
setField(request, "refreshToken", "oldToken");
when(userService.reissue(any())).thenReturn(new TokenResponseDto("newAccess", "newRefresh"));
mockMvc.perform(post("/api/v1/users/reissue")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.accessToken").value("newAccess"));
}
@Test
void logout() throws Exception {
AuthUser authUser = new AuthUser(1L, "user@email.com", "nickname", Role.ROLE_USER);
mockMvc.perform(post("/api/v1/users/logout")
.with(authentication(
new UsernamePasswordAuthenticationToken(
authUser,
null,
List.of(new SimpleGrantedAuthority("ROLE_USER"))
)
))
)
.andExpect(status().isOk());
verify(userService).logout(1L);
}
@Test
@DisplayName("회원 탈퇴 성공")
void withdraw() throws Exception {
WithdrawRequestDto request = new WithdrawRequestDto();
setField(request, "password", "Password123!");
AuthUser authUser = new AuthUser(1L, "user@email.com", "nickname", Role.ROLE_USER);
mockMvc.perform(delete("/api/v1/users/withdraw")
.with(authentication(
new UsernamePasswordAuthenticationToken(
authUser,
null,
List.of(new SimpleGrantedAuthority("ROLE_USER"))
)
))
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
)
.andExpect(status().isOk());
verify(userService).withdraw(eq(1L), eq("Password123!"));
}
}
3️⃣ RefreshTokenService
RefreshToken을 Redis에 저장, 조회, 삭제, 존재 여부 확인
더보기
class RefreshTokenServiceTest {
private RefreshTokenService refreshTokenService;
private StringRedisTemplate redisTemplate;
private ValueOperations<String, String> valueOperations;
@BeforeEach
void setUp() {
redisTemplate = mock(StringRedisTemplate.class);
valueOperations = mock(ValueOperations.class);
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
refreshTokenService = new RefreshTokenService(redisTemplate);
}
@Test
void save_정상저장_호출확인() {
Long userId = 1L;
String token = "sample-token";
Duration ttl = Duration.ofDays(7);
refreshTokenService.save(userId, token, ttl);
verify(valueOperations, times(1)).set("RT:" + userId, token, ttl);
}
@Test
void get_정상조회_반환값확인() {
Long userId = 1L;
String expected = "stored-token";
when(valueOperations.get("RT:" + userId)).thenReturn(expected);
String result = refreshTokenService.get(userId);
assertEquals(expected, result);
}
@Test
void delete_정상삭제_호출확인() {
Long userId = 1L;
refreshTokenService.delete(userId);
verify(redisTemplate, times(1)).delete("RT:" + userId);
}
@Test
void exists_존재할때_true() {
Long userId = 1L;
when(redisTemplate.hasKey("RT:" + userId)).thenReturn(true);
assertTrue(refreshTokenService.exists(userId));
}
@Test
void exists_존재하지않을때_false() {
Long userId = 1L;
when(redisTemplate.hasKey("RT:" + userId)).thenReturn(false);
assertFalse(refreshTokenService.exists(userId));
}
}
'TIL' 카테고리의 다른 글
| [250429 TIL] 배달 어플 아웃소싱 프로젝트 회고 (1) | 2025.04.29 |
|---|---|
| [250425] 배달어플 알림(SSE) + 토큰 쿠키 리팩토링 (2) | 2025.04.25 |
| [250423 TIL] 배달어플 소셜로그인 (1) | 2025.04.23 |
| [250422 TIL] 배달어플 JWT 회원 기능 초본 (2) | 2025.04.22 |
| [250416] 스프링 심화 과제 회고 (2) | 2025.04.16 |