항해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());
}
}