post 삭제와 수정에 대한 테스트 코드를 추가하였다.
두 method의 로직이 비슷하여 테스트 코드가 거의 비슷하여 비교적 편했다...
1. PostControllerTest
@WebMvcTest(PostController.class)
class PostControllerTest {
@Autowired
MockMvc mockMvc;
@MockBean
PostService postService;
@Autowired
ObjectMapper objectMapper;
...
@Test
@DisplayName("post 수정 성공")
@WithMockUser
void post_modify_success() throws Exception {
PostModifyRequest postModifyRequest = PostModifyRequest.builder()
.title("title")
.body("body")
.build();
PostDto modifiedPost = PostDto.builder()
.id(1)
.title(postModifyRequest.getTitle())
.body(postModifyRequest.getBody())
.build();
when(postService.modifyPost(any(), any(), any(), any())).thenReturn(modifiedPost);
mockMvc.perform(put("/api/v1/posts/1")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(postModifyRequest)))
.andExpect(jsonPath("$.resultCode").value("SUCCESS"))
.andExpect(jsonPath("$.result.message").value("포스트 수정 완료"))
.andExpect(jsonPath("$.result.postId").value(modifiedPost.getId()))
.andExpect(status().isOk())
.andDo(print());
}
@Test
@DisplayName("post 수정 실패 - 인증 실패")
@WithAnonymousUser
void post_modify_fail1() throws Exception {
PostModifyRequest postModifyRequest = PostModifyRequest.builder()
.title("title")
.body("body")
.build();
mockMvc.perform(put("/api/v1/posts/1")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(postModifyRequest)))
.andExpect(status().isUnauthorized())
.andDo(print());
}
@Test
@DisplayName("post 수정 실패 - 작성자 불일치")
@WithMockUser
void post_modify_fail2() throws Exception {
PostModifyRequest postModifyRequest = PostModifyRequest.builder()
.title("title")
.body("body")
.build();
when(postService.modifyPost(any(), any(), any(), any()))
.thenThrow(new AppException(ErrorCode.INVALID_PERMISSION, ""));
mockMvc.perform(put("/api/v1/posts/1")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(postModifyRequest)))
.andExpect(status().is(ErrorCode.INVALID_PERMISSION.getStatus().value()))
.andExpect(jsonPath("$.resultCode").value("ERROR"))
.andExpect(jsonPath("$.result.message").value(ErrorCode.INVALID_PERMISSION.getMessage()))
.andExpect(jsonPath("$.result.errorCode").value(ErrorCode.INVALID_PERMISSION.name()))
.andDo(print());
}
@Test
@DisplayName("post 수정 실패 - 데이터 베이스 에러")
@WithMockUser
void post_modify_fail3() throws Exception {
PostModifyRequest postModifyRequest = PostModifyRequest.builder()
.title("title")
.body("body")
.build();
when(postService.modifyPost(any(), any(), any(), any()))
.thenThrow(new AppException(ErrorCode.DATABASE_ERROR, ""));
mockMvc.perform(put("/api/v1/posts/1")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(postModifyRequest)))
.andExpect(status().is(ErrorCode.DATABASE_ERROR.getStatus().value()))
.andExpect(jsonPath("$.resultCode").value("ERROR"))
.andExpect(jsonPath("$.result.message").value(ErrorCode.DATABASE_ERROR.getMessage()))
.andExpect(jsonPath("$.result.errorCode").value(ErrorCode.DATABASE_ERROR.name()))
.andDo(print());
}
@Test
@DisplayName("post 삭제 성공")
@WithMockUser
void post_delete_success() throws Exception {
mockMvc.perform(delete("/api/v1/posts/1")
.with(csrf()))
.andExpect(jsonPath("$.resultCode").value("SUCCESS"))
.andExpect(jsonPath("$.result.message").value("포스트 삭제 완료"))
.andExpect(status().isOk())
.andDo(print());
}
@Test
@DisplayName("포스트 삭제 실패 - 인증 실패")
@WithAnonymousUser
void post_delete_fail1() throws Exception {
mockMvc.perform(delete("/api/v1/posts/1")
.with(csrf()))
.andExpect(status().isUnauthorized())
.andDo(print());
}
@Test
@DisplayName("포스트 삭제 실패 - 작성자 불일치")
@WithMockUser
void post_delete_fail2() throws Exception {
doThrow(new AppException(ErrorCode.INVALID_PERMISSION, "")).when(postService).deletePost(any(),any());
mockMvc.perform(delete("/api/v1/posts/1")
.with(csrf()))
.andExpect(status().is(ErrorCode.INVALID_PERMISSION.getStatus().value()))
.andExpect(jsonPath("$.resultCode").value("ERROR"))
.andExpect(jsonPath("$.result.message").value(ErrorCode.INVALID_PERMISSION.getMessage()))
.andExpect(jsonPath("$.result.errorCode").value(ErrorCode.INVALID_PERMISSION.name()))
.andDo(print());
}
@Test
@DisplayName("포스트 삭제 실패 - 데이터베이스 에러")
@WithMockUser
void post_delete_fail3() throws Exception {
doThrow(new AppException(ErrorCode.DATABASE_ERROR, "")).when(postService).deletePost(any(),any());
mockMvc.perform(delete("/api/v1/posts/1")
.with(csrf()))
.andExpect(jsonPath("$.resultCode").value("ERROR"))
.andExpect(jsonPath("$.result.message").value(ErrorCode.DATABASE_ERROR.getMessage()))
.andExpect(jsonPath("$.result.errorCode").value(ErrorCode.DATABASE_ERROR.name()))
.andDo(print());
}
}
수정/삭제 성공
- httpStatus가 200으로 정상 작동하는지 체크
- resultCode : "SUCCESS"로 출력되는지 체크
- result : message가 controller에서 설정한 대로 출력되는지 체크
- result : 수정 시, 출력되는 postId가 수정한 post의 id와 동일한지 체크
수정/삭제 실패
- 인증 실패, 작성자와 수정자(삭제자) 불일치, 데이터베이스 에러에 대한 test
- httpStatus가 ErrorCode enum에서 설정한 status와 동일한지 체크
- resultCode : "ERROR"로 출력되는지 체크
- result : message와 errorCode가 ErrorCode enum 에서 설정한 대로 출력되는지 체크
포스트 삭제 시 service에서 method가 void이기 때문에, service의 exception에 대한 설정을 할 때 기존과 차이가 있었다.
방법은 아래의 Reference를 참고하였다.
<Reference>
https://stackoverflow.com/questions/15156857/mockito-test-a-void-method-throws-an-exception
2. PostServiceTest
public class PostServiceTest {
PostService postService;
PostRepository postRepository = mock(PostRepository.class);
UserRepository userRepository = Mockito.mock(UserRepository.class);
@BeforeEach
void setUp() {
postService = new PostService(postRepository, userRepository);
}
...
@Test
@DisplayName("수정 실패 - 포스트 존재하지 않음")
@WithMockUser
void post_modify_fail1() {
User user = User.builder()
.id(1)
.userName("userName")
.password("password")
.role(UserRole.USER)
.build();
Post post = Post.builder()
.id(1)
.user(user)
.title("title")
.body("body")
.build();
when(postRepository.findById(post.getId())).thenReturn(Optional.empty());
AppException exception = assertThrows(AppException.class,
()-> {
postService.modifyPost(post.getId(), post.getTitle(), post.getBody(), user.getUserName());
});
Assertions.assertEquals("POST_NOT_FOUND", exception.getErrorCode().name());
}
@Test
@DisplayName("수정 실패 - 작성자!=유저")
@WithMockUser
void post_modify_fail2() {
//유저
User user = User.builder()
.id(1)
.userName("userName")
.password("password")
.role(UserRole.USER)
.build();
//작성자
User postCreator = User.builder()
.id(2)
.userName("userName2")
.password("password2")
.role(UserRole.USER)
.build();
Post post = Post.builder()
.id(1)
.user(postCreator)
.title("title")
.body("body")
.build();
when(postRepository.findById(post.getId())).thenReturn(Optional.of(post));
when(userRepository.findByUserName(user.getUserName())).thenReturn(Optional.of(user));
AppException exception = assertThrows(AppException.class,
()-> {
postService.modifyPost(post.getId(), post.getTitle(), post.getBody(), user.getUserName());
});
Assertions.assertEquals("INVALID_PERMISSION", exception.getErrorCode().name());
}
@Test
@DisplayName("수정 실패 - 유저 존재하지 않음")
@WithMockUser
void post_modify_fail3() {
User user = User.builder()
.id(1)
.userName("userName")
.password("password")
.role(UserRole.USER)
.build();
Post post = Post.builder()
.id(1)
.user(user)
.title("title")
.body("body")
.build();
when(postRepository.findById(post.getId())).thenReturn(Optional.of(post));
when(userRepository.findByUserName(user.getUserName())).thenReturn(Optional.empty());
AppException exception = assertThrows(AppException.class,
()-> {
postService.modifyPost(post.getId(), post.getTitle(), post.getBody(), user.getUserName());
});
Assertions.assertEquals("NOT_FOUNDED_USER_NAME", exception.getErrorCode().name());
}
@Test
@DisplayName("삭제 실패 - 유저 존재하지 않음")
@WithMockUser
void post_delete_fail1() {
User user = User.builder()
.id(1)
.userName("userName")
.password("password")
.role(UserRole.USER)
.build();
Post post = Post.builder()
.id(1)
.user(user)
.title("title")
.body("body")
.build();
when(postRepository.findById(post.getId())).thenReturn(Optional.of(post));
when(userRepository.findByUserName(user.getUserName())).thenReturn(Optional.empty());
AppException exception = assertThrows(AppException.class,
()-> {
postService.deletePost(user.getUserName(), post.getId());
});
Assertions.assertEquals("NOT_FOUNDED_USER_NAME", exception.getErrorCode().name());
}
@Test
@DisplayName("삭제 실패 - 포스트 존재하지 않음")
@WithMockUser
void post_delete_fail2() {
User user = User.builder()
.id(1)
.userName("userName")
.password("password")
.role(UserRole.USER)
.build();
Post post = Post.builder()
.id(1)
.user(user)
.title("title")
.body("body")
.build();
when(postRepository.findById(post.getId())).thenReturn(Optional.empty());
AppException exception = assertThrows(AppException.class,
()-> {
postService.deletePost(user.getUserName(), post.getId());
});
Assertions.assertEquals("POST_NOT_FOUND", exception.getErrorCode().name());
}
@Test
@DisplayName("삭제 실패 - 작성자!=유저")
@WithMockUser
void post_delete_fail3() {
//유저
User user = User.builder()
.id(1)
.userName("userName")
.password("password")
.role(UserRole.USER)
.build();
//작성자
User postCreator = User.builder()
.id(2)
.userName("userName2")
.password("password2")
.role(UserRole.USER)
.build();
Post post = Post.builder()
.id(1)
.user(postCreator)
.title("title")
.body("body")
.build();
when(postRepository.findById(post.getId())).thenReturn(Optional.of(post));
when(userRepository.findByUserName(user.getUserName())).thenReturn(Optional.of(user));
AppException exception = assertThrows(AppException.class,
()-> {
postService.deletePost(user.getUserName(), post.getId());
});
Assertions.assertEquals("INVALID_PERMISSION", exception.getErrorCode().name());
}
}
수정/삭제 실패
- 포스트 존재 x, 유저 존재 x, 작성자와 수정자(삭제자) 불일치 시 exception test
- 발생시킨 exception과 ErrorCode enum 에서 설정한 것과 동일한지 체크
개인 프로젝트 중간 점검 시점이 되었다.
테스트 코드에 좀 익숙해 진 듯 하지만 아직은 어려움이 많은 듯 하다.
mock, mockito 같은 가짜 객체에 대한 사용법에 대해서 좀 더 공부가 필요 할 듯 하다.
<전체 Reference>
https://stackoverflow.com/questions/15156857/mockito-test-a-void-method-throws-an-exception
'프로젝트 > 멋사 개인 프로젝트 (mutsa-SNS)' 카테고리의 다른 글
[14] mutsa-SNS 7일차 - 코드 리펙토링 (0) | 2022.12.28 |
---|---|
[13] mutsa-SNS 6일차 - (2) Admin 권한 부여 기능 추가 (2) | 2022.12.27 |
[11] mutsa-SNS 5일차 - (2) Post 삭제, 수정 기능 추가 (0) | 2022.12.26 |
[10] mutsa-SNS 5일차 - (1) Post 조회 기능 TestCode 추가 (0) | 2022.12.26 |
[09] mutsa-SNS 4일차 - Post 조회 기능 추가 (0) | 2022.12.24 |