이번에는 게시글 컨트롤러와 컨트롤러 테스트에 내용을 추가한다.
컨트롤러에서는 이제 저번에 구현했던 서비스를 본격적으로 사용할 것이다.
ArticleController
private final ArticleService articleService;
서비스를 읽어서 정보전달을 하기위해 작성했지만 컨트롤러 테스트를 진행할때는 배제를 시켜서 MockMvc가 순수하게 api의 입출력만을 확인하기 위해 @MockBean을 사용해서 서비스를 배제한다.
ArticleControllerTest
@MockBean private ArticleService articleService;
게시글 리스트를 조회하기 위해서 articleService
로부터 게시글을 받아와야한다.
처음에 게시글 리스트를 조회하게 되면 searchArticles
에서 searchType과 searchKeyword의 값이 모두 null상태에서 조회를 시작하게 된다.
given(articleService.searchArticles(eq(null),eq(null),any(Pageable.class))).willReturn(Page.empty());
필드중 일부만 matcher가 불가능하기 때문에 null 대신에 eq(null)로 수정하면 오류가 발생하지않는다.
이제 뷰에서 데이터,뷰의 존재여부, 그리고 모델 어트리뷰트로 넣어준 데이터 존재여부 검사를 하고 난다음에 articleService
에서 searchArticles
가 호출되었는지 확인한다.
public void givenNothing_whenRequestingArticlesView_thenReturnsArticlesView() throws Exception {
// Given
given(articleService.searchArticles(eq(null),eq(null),any(Pageable.class))).willReturn(Page.empty());
// When & Then
mvc.perform(get("/articles"))
.andExpect(status().isOk()) // 정상 호출
.andExpect(result -> content().contentType(MediaType.TEXT_HTML)) // 데이터 확인
.andExpect(view().name("articles/index")) // 뷰의 존재여부 검사
.andExpect(model().attributeExists("articles")); // 뷰에 모델 어트리뷰트로 넣어준 데이터존재 여부 검사
then(articleService).should().searchArticles(eq(null),eq(null),any(Pageable.class));
}
게시글 id를 임의로 선언하고, 게시글과 댓글관련 데이터가 들어있는 Dto를 생성하는 메소드를 호출한다. 해당 메소드는 이전에 서비스 테스트에서 사용한 픽스쳐를 사용하면 된다. mvc.perform에서 경로를 1에서 articleId로 수정해준다.
public void givenNothing_whenRequestingArticleView_thenReturnsArticleView() throws Exception {
// Given
Long articleId = 1L;
given(articleService.getArticle(articleId)).willReturn(createArticleCommentsDto());
// When & Then
mvc.perform(get("/articles/" + articleId))
.andExpect(status().isOk()) // 정상 호출
.andExpect(result -> content().contentType(MediaType.TEXT_HTML)) // 데이터 확인
.andExpect(view().name("articles/detail")) // 뷰의 존재여부 검사
.andExpect(model().attributeExists("article")) // 뷰에 모델 어트리뷰트로 넣어준 데이터존재 여부 검사
.andExpect(model().attributeExists("articleComments"))
then(articleService).should().getArticle(articleId);
}
private ArticleWithCommentsDto createArticleCommentsDto() {
return ArticleWithCommentsDto.of(
1L,
createUserAccountDto(),
Set.of(),
"title",
"content",
"#java",
LocalDateTime.now(),
"jyc",
LocalDateTime.now(),
"jyc"
);
}
private UserAccountDto createUserAccountDto() {
return UserAccountDto.of(
1L,
"jyc",
"pw",
"jyc@mail.com",
"Jyc",
"memo",
LocalDateTime.now(),
"jyc",
LocalDateTime.now(),
"jyc"
);
}
게시글 검색 전용 페이지는 추후에 작성할 예정.
이제 컨트롤러에 내용을 추가해서 테스트를 통과시키게 만든다.
@RequestParam을 이용해서 getparameter를 불러온다. 처음에 조회를 할때는 검색타입이나, 검색어가 null일수 있기 때문에 required는 false로 지정한다. pageable의 경우 @PageableDefault를 사용해서 작성하고 페이지 기본값은 10개, 최신순으로 게시글을 정렬한 상태로 나타나게 설정했다.
@GetMapping
public String articles(
@RequestParam(required = false) SearchType searchType,
@RequestParam(required = false) String searchValue,
@PageableDefault(size = 10, sort ="createdAt", direction = Sort.Direction.DESC) Pageable pageable,
ModelMap map) {
map.addAttribute("articles", articleService.searchArticles(searchType,searchValue,pageable).map(ArticleResponse::from));
return "articles/index";
}
/{articleId}매핑에서 article의 어트리뷰트로 "article"이라는 가짜값을 집어넣었었는데, 이값 대신 ArticleWithCommentsResponse에서 데이터를 가져온다.
@GetMapping("/{articleId}")
public String article(@PathVariable Long articleId, ModelMap map) {
ArticleWithCommentsResponse article = ArticleWithCommentsResponse.from(articleService.getArticle(articleId));
map.addAttribute("article", article);
map.addAttribute("articleComments", article.articleCommentsResponse());
return "articles/detail";
}
package com.jycproject.bulletinboard.dto.response;
import com.jycproject.bulletinboard.dto.ArticleWithCommentsDto;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
public record ArticleWithCommentsResponse(
Long id,
String title,
String content,
String hashtag,
LocalDateTime createdAt,
String email,
String nickname,
Set<ArticleCommentResponse> articleCommentsResponse
) implements Serializable {
public static ArticleWithCommentsResponse of(Long id, String title, String content, String hashtag, LocalDateTime createdAt, String email, String nickname, Set<ArticleCommentResponse> articleCommentResponse) {
return new ArticleWithCommentsResponse(id, title, content, hashtag, createdAt, email, nickname, articleCommentResponse);
}
public static ArticleWithCommentsResponse from(ArticleWithCommentsDto dto){
String nickname = dto.userAccountDto().nickname();
if (nickname == null || nickname.isBlank()) {
nickname = dto.userAccountDto().userId();
}
return new ArticleWithCommentsResponse(
dto.id(),
dto.title(),
dto.content(),
dto.hashtag(),
dto.createdAt(),
dto.userAccountDto().email(),
nickname,
dto.articleCommentDtos().stream()
.map(ArticleCommentResponse::from)
.collect(Collectors.toCollection(LinkedHashSet::new))
);
}
}
이제 컨트롤러에서 실질적으로 데이터를 올려주는 테스트는 통과했다.