항해99

23.10.25 항해 99 16기 실전 프로젝트 19일차

김용글 2023. 10. 25. 22:30

오늘 공부한 것

* 댓글 및 대댓글 기능 코드 수정  (Response 값에 nickname 추가)

* 댓글 및 대댓글 Controller 테스트 코드 작성

 

오늘은 어제 이어서 코드 수정을 조금하였다

프론트엔드와 소통 미스로 email로 변경하였던 nickname 값을 추가하는 작업을 했다

 

또한, 테스트 코드를 처음 작성해보아서 엄청 고생을했다

트러블 슈팅은 오늘 날짜로 따로 정리두었다

 

1. 댓글 및 대댓글 기능 코드 수정

 1) 댓글

더보기

(1) Dto

       nickname 을 추가 했으며, String users 변수를 comments 로 바꿧다

@Getter
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CommentsResponseDto {
    private Long commentId;
    private String contents;
    private String email;
    private String nickname;
    private String checkUser;
    private LocalDateTime createAt;
    private LocalDateTime modifiedAt;



    public CommentsResponseDto(Comments comments) {
        this.commentId = comments.getId();
        this.contents = comments.getContents();
        this.createAt = comments.getCreatedAt();
        this.modifiedAt = comments.getModifiedAt();
        this.email = comments.getEmail();
        this.nickname = comments.getNickname();
    }

    public CommentsResponseDto(Comments comments, String checkUser) {
        this.commentId = comments.getId();
        this.contents = comments.getContents();
        this.createAt = comments.getCreatedAt();
        this.modifiedAt = comments.getModifiedAt();
        this.email = comments.getEmail();
        this.nickname = comments.getNickname();
        this.checkUser = checkUser;
    }
}

 

  (2) Entity

        nickname 을 추가했다

@Entity
@Getter
@NoArgsConstructor
@Table(name = "comments")
public class Comments extends TimeStamped {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name ="comments_id")
    private  Long id;

    private String email;

    private String nickname;

    @Column(nullable = false, length = 500)
    private String contents;

    @ManyToOne
    @JoinColumn(name = "posts_id")
    private Posts posts;

    @OneToMany (mappedBy = "comments", orphanRemoval = true)
    private List<Replies> repliesList= new ArrayList<>();


    public Comments(CommentsRequestDto requestDto, Users users, Posts posts) {
        this.contents = requestDto.getContents();
        this.posts = posts;
        this.email = users.getEmail();
        this.nickname = users.getNickName();
    }

    public void update(CommentsRequestDto requestDto, Users users) {
        this.contents = requestDto.getContents();
        this.email = users.getEmail();
        this.nickname = users.getNickName();
    }
}

 

  (3) Service

    // 댓글 조회
    public Slice<CommentsResponseDto> commentsList(Long postId,
                                                   Pageable pageable) {

        Posts posts = postsRepository.findById(postId).orElseThrow(
                () -> new CustomException(ErrorCode.POST_NOT_EXIST)); // 존재하지 않는 게시글입니다

        Slice<Comments> commentsList = commentsRepository.findByPosts_IdOrderByCreatedAtDesc(postId, pageable);

        if (commentsList.isEmpty()) {
            throw new CustomException(ErrorCode.COMMENTS_NOT_EXIST); // 존재하지 않는 댓글입니다
        }

        List<CommentsResponseDto> commentsResponseDtoList = new ArrayList<>();

        for (Comments comments : commentsList) {
            if (posts.getUsers().getEmail().equals(comments.getEmail())) {
                commentsResponseDtoList.add(new CommentsResponseDto(comments, "글쓴이"));
            } else {
                commentsResponseDtoList.add(new CommentsResponseDto(comments));
            }
        }
        return new SliceImpl<>(commentsResponseDtoList, pageable, commentsList.hasNext());
    }

  

 2) 대댓글

더보기

  (1) Dto

       nickname 을 추가 했으며, String users 변수를 replies로 바꿧다

@Getter
@JsonInclude(JsonInclude.Include.NON_NULL)
public class RepliesResponseDto {
    private Long repliesId;
    private String contents;
    private String email;
    private String nickname;
    private String checkUser;
    private LocalDateTime createAt;
    private LocalDateTime modifiedAt;

    public RepliesResponseDto(Replies replies) {
        this.repliesId = replies.getId();
        this.contents = replies.getContents();
        this.createAt = replies.getCreatedAt();
        this.modifiedAt = replies.getModifiedAt();
        this.email = replies.getEmail();
        this.nickname = replies.getNickname();
    }

    public RepliesResponseDto(Replies replies, String checkUser) {
        this.repliesId = replies.getId();
        this.contents = replies.getContents();
        this.createAt = replies.getCreatedAt();
        this.modifiedAt = replies.getModifiedAt();
        this.email = replies.getEmail();
        this.nickname = replies.getNickname();
        this.checkUser = checkUser;
    }
}

 

  (2) Entity

        nickname 을 추가했다

@Entity
@Getter
@NoArgsConstructor
@Table(name = "replies")
public class Replies extends TimeStamped {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name ="replies_id")
    private Long id;

    private String email;

    private String nickname;

    @Column(nullable = false, length = 500)
    private String contents;

    @ManyToOne
    @JoinColumn(name = "comments_id")
    private Comments comments;

    public Replies(RepliesRequestDto requestDto, Users users, Comments comments) {
        this.contents = requestDto.getContents();
        this.comments = comments;
        this.email = users.getEmail();
        this.nickname = users.getNickName();
    }

    public void update(RepliesRequestDto requestDto, Users users) {
        this.contents = requestDto.getContents();
        this.email = users.getEmail();
        this.nickname = users.getNickName();
    }
}

 

  (3) Service

    // 대댓글 조회
    public Slice<RepliesResponseDto> repliesList(Long commentId,
                                                 Pageable pageable) {

        Comments comments = commentsRepository.findById(commentId).orElseThrow(
                () -> new CustomException(ErrorCode.COMMENTS_NOT_EXIST)); // 존재하지 않는 댓글입니다

        Posts posts = comments.getPosts();

        Slice<Replies> repliesList = repliesRepository.findByComments_IdOrderByCreatedAtDesc(commentId, pageable);

        if (repliesList.isEmpty()) {
            throw new CustomException(ErrorCode.REPLIES_NOT_EXIST); // 존재하지 않는 대댓글입니다
        }

        List<RepliesResponseDto> RepliesResponseDtoList = new ArrayList<>();

        for (Replies replies : repliesList) {
            if (posts.getUsers().getEmail().equals(replies.getEmail())) {
                RepliesResponseDtoList.add(new RepliesResponseDto(replies, "글쓴이"));
            } else {
                RepliesResponseDtoList.add(new RepliesResponseDto(replies));
            }
        }
        return new SliceImpl<>(RepliesResponseDtoList, pageable, repliesList.hasNext());
    }

 

2. 댓글 및 대댓글 Controller 테스트 코드 작성

 1) 댓글

더보기

(1) Dto

@Getter
@AllArgsConstructor // test 에 사용
@NoArgsConstructor // test 에 사용
public class CommentsRequestDto {
    private String contents;
}

 

  (2) CommentsControllerTest

@WebMvcTest (
        controllers = {CommentsController.class},
        // 제외 할 것 지정
        excludeFilters = {
                @ComponentScan.Filter(
                        type = FilterType.ASSIGNABLE_TYPE,
                        classes = WebSecurityConfig.class
                )
        }
)

class CommentsControllerTest {

    // 필요한 의존 객체의 타입에 해당하는 빈을 찾아 주입
    @Autowired
    private MockMvc mvc;

    private Principal principal;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    CommentsController commentsController;

    // 테스트할 USER 객체
    private void mockUserSetup() {

        // Mock 테스트 유저 생성
        UserDetails userDetails = User.withUsername("rkawk@gmail.com")
                .password("kim881012!!!")
                .roles("USER") // 롤에 대한 설정
                .build();
        principal = new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
    }

    @WithMockUser
    @Test
    @DisplayName("댓글 생성")
    public void commentsCreateTest() throws Exception {
        mockUserSetup();

        // Given
        Long postId = 1L;
        String contents = "댓글";
        CommentsRequestDto requestDto = new CommentsRequestDto(contents);

        // eq : 특정한 값을 기대하는 경우에 사용됨
        // any : 어떤 값이든 허용하는 경우에 사용됨
        when(commentsController.commentsCreate(eq(postId), any(CommentsRequestDto.class), any(UserDetailsImpl.class)))
                .thenReturn(new ResponseEntity<>(new MessageResponseDto("댓글을 작성하였습니다.", 200), HttpStatus.OK));

        // Wen and Then
        mvc.perform(MockMvcRequestBuilders.post("/api/posts/"+postId+"/comments")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(requestDto))
                        .with(csrf()) // CSRF 토큰을 요청에 포함
                        .principal(principal)) // 가짜 사용자 principal 설정
                        .andExpect(status().isOk())
                        .andDo(print());
    }

    @WithMockUser
    @Test
    @DisplayName("댓글 조회")
    public void commentsListTest() throws Exception {

        // Given
        Long postId = 1L;
        Comments comments1 = new Comments();
        Comments comments2 = new Comments();

        List<CommentsResponseDto> responseDto = Arrays.asList(
                new CommentsResponseDto(comments1),
                new CommentsResponseDto(comments2)
        );

        // eq : 특정한 값을 기대하는 경우에 사용됨
        // any : 어떤 값이든 허용하는 경우에 사용됨
        when(commentsController.commentsList(eq(postId), any(Pageable.class)))
                .thenReturn(new ResponseEntity<>(HttpStatus.OK));

        // Wen and Then
        mvc.perform(MockMvcRequestBuilders.get("/api/posts/" + postId + "/comments")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(responseDto)))
                        .andExpect(status().isOk())
                        .andDo(print());
    }

    @WithMockUser
    @Test
    @DisplayName("마이페이지에서 내가 쓴 댓글 조회")
    public void commentsMeListTest() throws Exception {
        mockUserSetup();

        // Given
        Comments comments1 = new Comments();
        Comments comments2 = new Comments();

        List<CommentsResponseDto> responseDto = Arrays.asList(
                new CommentsResponseDto(comments1),
                new CommentsResponseDto(comments2)
        );

        // eq : 특정한 값을 기대하는 경우에 사용됨
        // any : 어떤 값이든 허용하는 경우에 사용됨
        when(commentsController.commentsMeList(any(UserDetailsImpl.class), any(Pageable.class)))
                .thenReturn(new ResponseEntity<>(HttpStatus.OK));

        // Wen and Then
        mvc.perform(MockMvcRequestBuilders.get("/api/commentsme")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(responseDto))
                        .with(csrf()) // CSRF 토큰을 요청에 포함
                        .principal(principal)) // 가짜 사용자 principal 설정
                        .andExpect(status().isOk())
                        .andDo(print());
    }

    @WithMockUser
    @Test
    @DisplayName("댓글 수정")
    public void commentsUpdateTest() throws Exception {
        mockUserSetup();

        // Given
        Long commentId = 1L;
        String contents = "댓글";
        CommentsRequestDto requestDto = new CommentsRequestDto(contents);

        // eq : 특정한 값을 기대하는 경우에 사용됨
        // any : 어떤 값이든 허용하는 경우에 사용됨
        when(commentsController.commentsUpdate(eq(commentId), any(CommentsRequestDto.class), any(UserDetailsImpl.class)))
                .thenReturn(new ResponseEntity<>(new MessageResponseDto("댓글을 수정하였습니다.", 200), HttpStatus.OK));

        // Wen and Then
        mvc.perform(MockMvcRequestBuilders.put("/api/comments/"+commentId)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(requestDto))
                        .with(csrf()) // CSRF 토큰을 요청에 포함
                        .principal(principal)) // 가짜 사용자 principal 설정
                        .andExpect(status().isOk())
                        .andDo(print());
    }

    @WithMockUser
    @Test
    @DisplayName("댓글 삭제")
    public void commentsDeleteTest() throws Exception {
        mockUserSetup();

        // Given
        Long commentId = 1L;

        // eq : 특정한 값을 기대하는 경우에 사용됨
        // any : 어떤 값이든 허용하는 경우에 사용됨
        when(commentsController.commentsDelete(eq(commentId), any(UserDetailsImpl.class)))
                .thenReturn(new ResponseEntity<>(new MessageResponseDto("댓글을 삭제하였습니다.", 200), HttpStatus.OK));

        // Wen and Then
        mvc.perform(MockMvcRequestBuilders.delete("/api/comments/"+commentId)
                        .contentType(MediaType.APPLICATION_JSON)
                        .with(csrf()) // CSRF 토큰을 요청에 포함
                        .principal(principal)) // 가짜 사용자 principal 설정
                        .andExpect(status().isOk())
                        .andDo(print());
    }
}

  

 2) 대댓글

더보기

(1) Dto

@Getter
@AllArgsConstructor // test 에 사용
@NoArgsConstructor // test 에 사용
public class RepliesRequestDto {
    private String contents;
}

 

  (2) RepliesControllerTest

@WebMvcTest (
        controllers = {RepliesController.class},
        // 제외 할 것 지정
        excludeFilters = {
                @ComponentScan.Filter(
                        type = FilterType.ASSIGNABLE_TYPE,
                        classes = WebSecurityConfig.class
                )
        }
)

class RepliesControllerTest {

    // 필요한 의존 객체의 타입에 해당하는 빈을 찾아 주입
    @Autowired
    private MockMvc mvc;

    private Principal principal;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    RepliesController repliesController;

    // 테스트할 USER 객체
    private void mockUserSetup() {

        // Mock 테스트 유저 생성
        UserDetails userDetails = User.withUsername("rkawk@gmail.com")
                .password("kim881012!!!")
                .roles("USER") // 롤에 대한 설정
                .build();
        principal = new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
    }

    @WithMockUser
    @Test
    @DisplayName("대댓글 생성")
    public void repliesCreateTest() throws Exception {
        mockUserSetup();

        // Given
        Long commentId = 1L;
        String contents = "대댓글";
        RepliesRequestDto requestDto = new RepliesRequestDto(contents);

        // eq : 특정한 값을 기대하는 경우에 사용됨
        // any : 어떤 값이든 허용하는 경우에 사용됨
        when(repliesController.repliesCreate(eq(commentId), any(RepliesRequestDto.class), any(UserDetailsImpl.class)))
                .thenReturn(new ResponseEntity<>(new MessageResponseDto("대댓글을 작성하였습니다", 200), HttpStatus.OK));

        // Wen and Then
        mvc.perform(MockMvcRequestBuilders.post("/api/comments/" + commentId + "/replies")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(requestDto))
                        .with(csrf()) // CSRF 토큰을 요청에 포함
                        .principal(principal)) // 가짜 사용자 principal 설정
                .andExpect(status().isOk())
                .andDo(print());
    }

    @WithMockUser
    @Test
    @DisplayName("대댓글 조회")
    public void repliesListTest() throws Exception {

        // Given
        Long commentId = 1L;
        Replies replies1 = new Replies();
        Replies replies2 = new Replies();

        List<RepliesResponseDto> responseDto = Arrays.asList(
                new RepliesResponseDto(replies1),
                new RepliesResponseDto(replies2)
        );

        // eq : 특정한 값을 기대하는 경우에 사용됨
        // any : 어떤 값이든 허용하는 경우에 사용됨
        when(repliesController.repliesList(eq(commentId), any(Pageable.class)))
                .thenReturn(new ResponseEntity<>(HttpStatus.OK));

        // Wen and Then
        mvc.perform(MockMvcRequestBuilders.get("/api/comments/"+commentId+"/replies")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(responseDto)))
                        .andExpect(status().isOk())
                        .andDo(print());
    }

    @WithMockUser
    @Test
    @DisplayName("마이페이지에서 내가 쓴 댓글 조회")
    public void repliesMeListTest() throws Exception {
        mockUserSetup();

        // Given
        Replies replies1 = new Replies();
        Replies replies2 = new Replies();

        List<RepliesResponseDto> responseDto = Arrays.asList(
                new RepliesResponseDto(replies1),
                new RepliesResponseDto(replies2)
        );

        // eq : 특정한 값을 기대하는 경우에 사용됨
        // any : 어떤 값이든 허용하는 경우에 사용됨
        when(repliesController.repliesMeList(any(UserDetailsImpl.class), any(Pageable.class)))
                .thenReturn(new ResponseEntity<>(HttpStatus.OK));

        // Wen and Then
        mvc.perform(MockMvcRequestBuilders.get("/api/repliesme")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(responseDto))
                        .with(csrf()) // CSRF 토큰을 요청에 포함
                        .principal(principal)) // 가짜 사용자 principal 설정
                        .andExpect(status().isOk())
                        .andDo(print());
    }

    @WithMockUser
    @Test
    @DisplayName("대댓글 수정")
    public void repliesUpdateTest() throws Exception {
        mockUserSetup();

        // Given
        Long repliesId = 1L;
        String contents = "대댓글";
        RepliesRequestDto requestDto = new RepliesRequestDto(contents);

        // eq : 특정한 값을 기대하는 경우에 사용됨
        // any : 어떤 값이든 허용하는 경우에 사용됨
        when(repliesController.repliesUpdate(eq(repliesId), any(RepliesRequestDto.class), any(UserDetailsImpl.class)))
                .thenReturn(new ResponseEntity<>(new MessageResponseDto("대댓글을 수정하였습니다", 200), HttpStatus.OK));

        // Wen and Then
        mvc.perform(MockMvcRequestBuilders.put("/api/replies/" + repliesId)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(requestDto))
                        .with(csrf()) // CSRF 토큰을 요청에 포함
                        .principal(principal)) // 가짜 사용자 principal 설정
                .andExpect(status().isOk())
                .andDo(print());
    }

    @WithMockUser
    @Test
    @DisplayName("대댓글 삭제")
    public void repliesDeleteTest() throws Exception {
        mockUserSetup();

        // Given
        Long repliesId = 1L;

        // eq : 특정한 값을 기대하는 경우에 사용됨
        // any : 어떤 값이든 허용하는 경우에 사용됨
        when(repliesController.repliesDelete(eq(repliesId), any(UserDetailsImpl.class)))
                .thenReturn(new ResponseEntity<>(new MessageResponseDto("대댓글을 삭제하였습니다", 200), HttpStatus.OK));

        // Wen and Then
        mvc.perform(MockMvcRequestBuilders.delete("/api/replies/" + repliesId)
                        .contentType(MediaType.APPLICATION_JSON)
                        .with(csrf()) // CSRF 토큰을 요청에 포함
                        .principal(principal)) // 가짜 사용자 principal 설정
                .andExpect(status().isOk())
                .andDo(print());
    }
}