본문 바로가기

프로젝트/멋사 개인 프로젝트 (mutsa-SNS)

[12] mutsa-SNS 6일차 - (1) Post 삭제, 수정 Test Code 추가

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

 

Mockito test a void method throws an exception

I have a method with a void return type. It can also throw a number of exceptions so I'd like to test those exceptions being thrown. All attempts have failed with the same reason: The method whe...

stackoverflow.com

 

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