#16 board : article comment controller impl, test
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
package com.example.board.controller;
|
||||
|
||||
import com.example.board.dto.UserAccountDto;
|
||||
import com.example.board.dto.request.ArticleCommentRequest;
|
||||
import com.example.board.service.ArticleCommentService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/comments")
|
||||
@Controller
|
||||
public class ArticleCommentController {
|
||||
|
||||
private final ArticleCommentService articleCommentService;
|
||||
|
||||
@PostMapping("/new")
|
||||
public String postNewArticleComment(ArticleCommentRequest articleCommentRequest) {
|
||||
|
||||
// TODO: 인증 정보 필요
|
||||
articleCommentService.saveArticleComment(articleCommentRequest.toDto(
|
||||
UserAccountDto.of(
|
||||
"bobby",
|
||||
"1234",
|
||||
"bobby@email.com",
|
||||
null,
|
||||
null
|
||||
)
|
||||
));
|
||||
|
||||
return "redirect:/articles/" + articleCommentRequest.articleId();
|
||||
}
|
||||
|
||||
@PostMapping("/{commentId}/delete")
|
||||
public String deleteArticleComment(@PathVariable Long commentId,
|
||||
Long articleId) {
|
||||
|
||||
articleCommentService.deleteArticleComment(commentId);
|
||||
|
||||
return "redirect:/articles/" + articleId;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.example.board.dto;
|
||||
|
||||
import com.example.board.domain.Article;
|
||||
import com.example.board.domain.ArticleComment;
|
||||
import com.example.board.domain.UserAccount;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@@ -15,6 +16,11 @@ public record ArticleCommentDto(
|
||||
LocalDateTime modifiedAt,
|
||||
String modifiedBy
|
||||
) {
|
||||
|
||||
public static ArticleCommentDto of(Long articleId, UserAccountDto userAccountDto, String content) {
|
||||
return new ArticleCommentDto(null, articleId, userAccountDto, content, null, null, null, null);
|
||||
}
|
||||
|
||||
public static ArticleCommentDto of(Long id, Long articleId, UserAccountDto userAccountDto, String content, LocalDateTime createdAt, String createdBy, LocalDateTime modifiedAt, String modifiedBy) {
|
||||
return new ArticleCommentDto(id, articleId, userAccountDto, content, createdAt, createdBy, modifiedAt, modifiedBy);
|
||||
}
|
||||
@@ -32,10 +38,10 @@ public record ArticleCommentDto(
|
||||
);
|
||||
}
|
||||
|
||||
public ArticleComment toEntity(Article entity) {
|
||||
public ArticleComment toEntity(Article article, UserAccount userAccount) {
|
||||
return ArticleComment.of(
|
||||
entity,
|
||||
userAccountDto.toEntity(),
|
||||
article,
|
||||
userAccount,
|
||||
content
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.example.board.dto.request;
|
||||
|
||||
import com.example.board.dto.ArticleCommentDto;
|
||||
import com.example.board.dto.UserAccountDto;
|
||||
|
||||
public record ArticleCommentRequest(Long articleId, String content) {
|
||||
|
||||
public static ArticleCommentRequest of(Long articleId, String content) {
|
||||
return new ArticleCommentRequest(articleId, content);
|
||||
}
|
||||
|
||||
public ArticleCommentDto toDto(UserAccountDto userAccountDto) {
|
||||
return ArticleCommentDto.of(
|
||||
articleId,
|
||||
userAccountDto,
|
||||
content
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.example.board.service;
|
||||
|
||||
import com.example.board.domain.Article;
|
||||
import com.example.board.domain.ArticleComment;
|
||||
import com.example.board.domain.UserAccount;
|
||||
import com.example.board.dto.ArticleCommentDto;
|
||||
import com.example.board.repository.ArticleCommentRepository;
|
||||
import com.example.board.repository.ArticleRepository;
|
||||
import com.example.board.repository.UserAccountRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -20,6 +23,7 @@ public class ArticleCommentService {
|
||||
|
||||
private final ArticleRepository articleRepository;
|
||||
private final ArticleCommentRepository articleCommentRepository;
|
||||
private final UserAccountRepository userAccountRepository;
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<ArticleCommentDto> searchArticleComments(Long articleId) {
|
||||
@@ -31,9 +35,11 @@ public class ArticleCommentService {
|
||||
|
||||
public void saveArticleComment(ArticleCommentDto dto) {
|
||||
try {
|
||||
articleCommentRepository.save(dto.toEntity(articleRepository.getReferenceById(dto.articleId())));
|
||||
Article article = articleRepository.getReferenceById(dto.articleId());
|
||||
UserAccount userAccount = userAccountRepository.getReferenceById(dto.userAccountDto().userId());
|
||||
articleCommentRepository.save(dto.toEntity(article,userAccount));
|
||||
} catch (EntityNotFoundException e) {
|
||||
log.warn("댓글 저장 실패. 댓글의 게시글을 찾을 수 없습니다 - dto: {}", dto);
|
||||
log.warn("댓글 저장 실패. 댓글 작성에 필요한 정보를 찾을 수 없습니다 - dto: {}", e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.example.board.controller;
|
||||
|
||||
import com.example.board.config.SecurityConfig;
|
||||
import com.example.board.dto.ArticleCommentDto;
|
||||
import com.example.board.dto.request.ArticleCommentRequest;
|
||||
import com.example.board.service.ArticleCommentService;
|
||||
import com.example.board.util.FormDataEncoder;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.BDDMockito.willDoNothing;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@DisplayName("View 컨트롤러 - 댓글")
|
||||
@Import({SecurityConfig.class, FormDataEncoder.class})
|
||||
@WebMvcTest(ArticleCommentController.class)
|
||||
class ArticleCommentControllerTest {
|
||||
|
||||
private final MockMvc mockMvc;
|
||||
private final FormDataEncoder formDataEncoder;
|
||||
|
||||
@MockBean
|
||||
private ArticleCommentService articleCommentService;
|
||||
|
||||
public ArticleCommentControllerTest(
|
||||
@Autowired MockMvc mockMvc,
|
||||
@Autowired FormDataEncoder formDataEncoder
|
||||
) {
|
||||
this.mockMvc = mockMvc;
|
||||
this.formDataEncoder = formDataEncoder;
|
||||
}
|
||||
|
||||
@DisplayName("[view][POST] 댓글 등록 - 정상 호출")
|
||||
@Test
|
||||
void givenArticleCommentInfo_whenRequesting_thenSavesNewArticleComment() throws Exception {
|
||||
// Given
|
||||
long articleId = 1L;
|
||||
ArticleCommentRequest request = ArticleCommentRequest.of(articleId, "test comment");
|
||||
willDoNothing().given(articleCommentService).saveArticleComment(any(ArticleCommentDto.class));
|
||||
|
||||
// When & Then
|
||||
mockMvc.perform(
|
||||
post("/comments/new")
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
.content(formDataEncoder.encode(request))
|
||||
.with(csrf())
|
||||
)
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(view().name("redirect:/articles/" + articleId))
|
||||
.andExpect(redirectedUrl("/articles/" + articleId));
|
||||
|
||||
then(articleCommentService).should().saveArticleComment(any(ArticleCommentDto.class));
|
||||
}
|
||||
|
||||
@DisplayName("[view][POST] 댓글 삭제 - 정상 호출")
|
||||
@Test
|
||||
void givenArticleCommentIdToDelete_whenRequesting_thenDeletesArticleComment() throws Exception {
|
||||
// Given
|
||||
long articleId = 1L;
|
||||
long articleCommentId = 1L;
|
||||
String userId = "unoTest";
|
||||
willDoNothing().given(articleCommentService).deleteArticleComment(articleCommentId);
|
||||
|
||||
// When & Then
|
||||
mockMvc.perform(
|
||||
post("/comments/" + articleCommentId + "/delete")
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
.content(formDataEncoder.encode(Map.of("articleId", articleId)))
|
||||
.with(csrf())
|
||||
)
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(view().name("redirect:/articles/" + articleId))
|
||||
.andExpect(redirectedUrl("/articles/" + articleId));
|
||||
|
||||
then(articleCommentService).should().deleteArticleComment(articleCommentId);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import com.example.board.dto.ArticleCommentDto;
|
||||
import com.example.board.dto.UserAccountDto;
|
||||
import com.example.board.repository.ArticleCommentRepository;
|
||||
import com.example.board.repository.ArticleRepository;
|
||||
import com.example.board.repository.UserAccountRepository;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -34,6 +35,9 @@ class ArticleCommentServiceTest {
|
||||
@Mock
|
||||
private ArticleCommentRepository articleCommentRepository;
|
||||
|
||||
@Mock
|
||||
private UserAccountRepository userAccountRepository;
|
||||
|
||||
@DisplayName("게시글 ID로 조회하면 해당하는 댓글 리스트를 반환한다.")
|
||||
@Test
|
||||
void givenArticleId_whenSearchingArticleComments_thenReturnsArticleComments() {
|
||||
@@ -60,12 +64,15 @@ class ArticleCommentServiceTest {
|
||||
// Given
|
||||
ArticleCommentDto dto = createArticleCommentDto("댓글");
|
||||
given(articleRepository.getReferenceById(dto.articleId())).willReturn(createArticle());
|
||||
given(userAccountRepository.getReferenceById(dto.userAccountDto().userId())).willReturn(createUserAccount());
|
||||
given(articleCommentRepository.save(any(ArticleComment.class))).willReturn(null);
|
||||
|
||||
// When
|
||||
sut.saveArticleComment(dto);
|
||||
|
||||
// Then
|
||||
then(articleRepository).should().getReferenceById(dto.articleId());
|
||||
then(userAccountRepository).should().getReferenceById(dto.userAccountDto().userId());
|
||||
then(articleCommentRepository).should().save(any(ArticleComment.class));
|
||||
}
|
||||
|
||||
@@ -81,6 +88,7 @@ class ArticleCommentServiceTest {
|
||||
|
||||
// Then
|
||||
then(articleRepository).should().getReferenceById(dto.articleId());
|
||||
then(userAccountRepository).shouldHaveNoInteractions();
|
||||
then(articleCommentRepository).shouldHaveNoInteractions();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user