JPA

SpringBoot JPA 게시판 CRUD 구현(Create - TDD)

빈코 2022. 7. 18. 20:24

오늘은 지난번에 구현한 게시판 Create 로직의 테스트 케이스를 작성해보려 합니다. 테스트 케이스는 Controller 테스트와 Service 테스트를 구현하려 합니다. 이전 포스팅을 보지 않았다면, 이번 포스팅을 이해하기는 힘드실 것 같아요. 꼭 이전 포스팅을 보고 와주세요!

 

테스트 디렉토리는 java 하위에 있는 폴더 구조와 동일하게 구조를 맞추셔야 합니다. 폴더 구조와 gradle 빌드는 깃허브를 통해 확인하실 수 있습니다 :)

 

로고
게시판 Create TDD

 

Controller Test 생성📗

로직 테스트에 앞서 기본 클래스를 먼저 생성해줍니다. 앞서 말씀드린 것처럼 build(gradle, maven)에 test 관련 dependency가 추가 되어 있어야 합니다. 테스트 케이스가 처음이신 분들을 고려하여 import 부분도 올리겠습니다.

 

import org.junit.jupiter.api.BeforeEach;
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.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

 

@AutoConfigureMockMvc
@SpringBootTest
class PostControllerTest {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private PostRepository postRepository;

    @BeforeEach
    void clean() {
        postRepository.deleteAll();
    }
}

 

@AutoConfigureMockMvc 어노테이션은 웹 애플리케이션에서 컨트롤러를 테스트할 때, 서블릿 컨테이너를 모킹 하기 위해서 사용합니다. 비슷한 어노테이션으로 @WebMvcTest가 있지만,  @WebMvcTest @SpringBootTest와 같이 사용할 수 없습니다. 왜냐하면 각자 서로의 MockMvc를 모킹하여 충돌이 발생하기 때문입니다.

 

두 개의 어노테이션은 공통점과 각각의 차이점이 있지만, MockMvc를 보다 세밀하게 제어하기 위해@AutoConfigureMockMvc와 @SpringBootTest를 같이 선언하였다고 보시면 될 것 같습니다😃

 

ObjectMapper는 PostCreate 객체를 Json 문자열로 만들기 위함입니다. 클라이언트 단에서 컨트롤러 단으로 데이터가 넘어갈 때 Json 형식으로 넘어가기 때문에, 테스트 코드에서는 objectMapper를 통해 객체를 Json 문자열로 만들어 줍니다. 자세한 사용법은 하단에 포스팅할게요!

 

🔍각 어노테이션 설명

  • @Autowired : 필요한 의존 객체의 "타입"에 해당하는 빈을 찾아 주입하는 어노테이션입니다.
  • @BeforeEach : 본 어노테이션을 붙인 메서드는 테스트 메서드 실행 이전에 수행됩니다.

 

📌 BeforeEach를 사용한 이유는 각각의 테스트가 서로 영향을 받지 않기 위해서 이전에 수행한 테스트들의 남아 있는 값들을 deleteAll() 메서드를 사용하여 DB에서 모두 제거하기 위함입니다.

 

    @Test
    @DisplayName("/posts 요청 시 DB에 값이 저장된다.")
    void insertPost() throws Exception {
        // give
        PostCreate request = PostCreate.builder()
                .title("제목입니다.")
                .content("내용입니다.")
                .build();

        String json = objectMapper.writeValueAsString(request);

        // when
        mockMvc.perform(MockMvcRequestBuilders.post("/posts")
                        .contentType(APPLICATION_JSON)
                        .content(json)
                )
                .andExpect(status().isOk())
                .andDo(print());

        //then
        assertEquals(1L, postRepository.count());

        Post post = postRepository.findAll().get(0);
        assertEquals("제목입니다.", post.getTitle());
        assertEquals("내용입니다.", post.getContent());
 }

 

첫 번째 테스트는 실제로 DB에 값이 저장되는지 확인하는 테스트입니다. @Test 어노테이션을 붙이고, @DisplayName으로 해당 테스트가 어떤 테스트를 의미하는지 명시합니다. 이 명시된 제목은 테스트를 수행하였을 때, log에 남기 때문에 전체 테스트 수행 시 어떤 테스트에서 오류 나는지 확인하기 편합니다.

 

give section

give 단계에서는 실제 웹 프로젝트에서 클라이언트단에서 컨트롤러단으로 넘기는 데이터를 가공하는 부분이라고 생각하시면 됩니다. 이전 포스팅에서 구현한 PostCreate 클래스를 Builder 패턴을 이용하여 생성해줍니다.

 

또한, 앞서 언급드린 objectMapper를 이용하여 해당 객체를 Json 형태의 문자열로 치환해줍니다.

 

when section

when 단계에서는 url을 매핑하는 단계라고 생각하시면 됩니다. MockMvc.perform 메서드를 통해 해당 메서드의 방식(post)과 contentType(Json), content(이전에 치환한 객체)를 명시해줍니다. 

 

그리고 기대하는 Http Status Code를 작성합니다. status(). isOk()는 200 코드를 의미합니다. 흔히, 404는 찾을 수 없는 페이지, 500은 서버 에러 등 다양한 코드들이 있습니다.

 

StatusResultMatcher

기본적으로 제공하는 StatusResultMatcher 클래스를 살펴보시면 숫자 별로 다양하게 return 시켜주는 것을 확인할 수 있습니다. 각자의 상황에 맞게 code를 가져다 쓰시면 될 것 같네요😄

 

andDo는 요청에 대한 처리를 하며, print 메서드가 일반적으로 사용됩니다. print()를 사용하면 테스트 실행 시 로그에서 자세한 내용을 확인 할 수 있습니다.

 

then section

then 단계에서는 give 단계에서 작성한 객체와 개수 등 다양한 케이스를 작성하며 비교할 수 있습니다. assertEqual 메서드를 사용하여 JpaRepository에서 기본적으로 제공하는 count() 메서드를 이용하여 1개 값이 들어가 있는지 체크하는 로직을 넣었습니다.

 

그 이후에는 실제 저장했던 제목과 내용이 일치하는지 확인하였습니다. 이 부분에서도 JpaRepository가 제공하는 findAll() 메서드를 통해 데이터를 가져왔고, get(0) 번째를 꺼내서 해당 Post와 기대하는 값이 일치하는지 체크하였습니다.

 

[ 테스트 결과 ]

성공
성공!

 

Service Test 생성📘

이어서 Service Test를 작성하려 합니다. 컨트롤러와 많이 유사하지만 실제로 url을 매핑해주지 않아도 되기 때문에 @MockMvc는 사용하지 않으며, 이에 따라 Json으로 치환하지 않아도 되므로 objectMapper도 사용하지 않는 차이점이 있습니다.

 

@SpringBootTest
class PostServiceTest {

    @Autowired
    private PostService postService;

    @Autowired
    private PostRepository postRepository;

    @BeforeEach
    void clean() {
        postRepository.deleteAll();
    }

    @Test
    @DisplayName("글 작성")
    void test1() {
        //given
        PostCreate postCreate = PostCreate.builder()
                .title("제목")
                .content("내용")
                .build();

        //when
        postService.write(postCreate);

        //then
        assertEquals(1L, postRepository.count());

        Post post = postRepository.findAll().get(0);
        assertEquals("제목",post.getTitle());
        assertEquals("내용",post.getContent());
    }
 }

 

컨트롤러 테스트와 마찬가지로 postService와 postRepository를 @Autowired를 통해 주입받습니다. 그리고 @BeforeEach 어노테이션을 이용해 각각의 테스트에 영향이 없도록 deleteAll() 메서드를 사용하였습니다.

 

given section

given 단계에서는 컨트롤러 테스트와 마찬가지로 글 작성용 DTO인 PostCreate 객체를 임의대로 Builder 패턴을 이용하여 생성합니다. 

 

when section

when 단계에서는 postService의 write() 메소드를 사용하여 given 단계에서 생성한 DTO 객체를 파라미터 값으로 넣어줍니다. 이전 포스팅에서 만든 postService의 write() 함수입니다.

 

public void write(PostCreate postCreate){
        Post post = Post.builder()
                .title(postCreate.getTitle())
                .content(postCreate.getContent())
                .build();
        postRepository.save(post);
}

 

then section

then 단계에서는 컨트롤러 테스트와 마찬가지로 count() 메소드를 사용해 결과를 비교하는 테스트 케이스와 DB의 첫 번째 값을 찾아 given 단계에서 등록한 제목과 내용이 일치하는지 테스트하는 케이스를 작성하였습니다.

 

테스트 결과
성공!

 


👨‍👩‍👦‍👦 오픈채팅방 운영

취업을 준비하는 예비 개발자분들을 위한 질문&답변할 수 있는 공간을 만들었습니다. 취업과 이직을 하기 위해서 어떤 걸 중점적으로 준비해야 하는지부터 포트폴리오&이력서 작성법 등 다양한 질문들을 받고 답변을 드립니다. 참여하셔서 다양한 정보 얻고 가시면 좋을 것 같네요😁

 

참여코드 : 456456

https://open.kakao.com/o/gVHZP8dg

 

비전공 개발자 취업 준비방(질문&답변)

#비전공 #개발자 #취업 #멘토링 #부트캠프 #국비지원 #백엔드 #프론트엔드 #중소기업 #중견기업 #자바 #Java #sql

open.kakao.com

 


👨‍💻 전자책 출간

아울러 제가  🌟비전공자에서 2년만에 보안 전문 중견기업으로 이직 한 방법들을 정리한 전자책을 출간 하게 되었습니다. 어떤 걸 공부해야 하는지, 이직을 위해서 무엇을 준비해야 하는지, 제가 받았던 기술 면접 리스트 등 다양한 목차로 구성되어 있습니다. 또한, 구매 시 1:1 채팅을 이용하여 포트폴리오 첨삭을 도와드리고 있습니다. 🐕전자책으로 얻은 모든 수익은 유기견 센터 '팅*벨 입양센터'에 후원될 예정입니다. 관심 있으신 분들은 아래 링크를 참고해주세요😁

https://kmong.com/gig/480954

 

비전공개발자 2년만에 중견기업 들어간 방법 | 14000원부터 시작 가능한 총 평점 0점의 전자책, 취

0개 총 작업 개수 완료한 총 평점 0점인 Binco의 전자책, 취업·이직 전자책 서비스를 0개의 리뷰와 함께 확인해 보세요. 전자책, 취업·이직 전자책 제공 등 14000원부터 시작 가능한 서비스

kmong.com


 

마치며

지금까지 게시판 작성에 대한 테스트 케이스를 작성하였습니다. 사실 포스팅에 한계가 있어 테스트를 다 작성하지 않았지만, 테스트 케이스는 실패하는 케이스와 성공하는 여러 케이스들을 작성해 봐야 합니다. 

 

컨트롤러와 서비스 테스트가 많이 유사하지만, 특히 컨트롤러 테스트에서 사용하는 objectMapper와 mockMvc는 포스팅하면서 조금 더 공부해야겠다는 필요성을 느끼네요! 다음 포스팅은 게시글 조회 로직을 구현해보겠습니다.

 

이전 포스팅

* [ SpringBoot JPA 게시판 CRUD(Create) ]

 

다음 포스팅

* [ SpringBoot JPA 게시판 CRUD(단건 조회 및 TDD) ]

* [ SpringBoot JPA 게시판 CRUD(Read - 다중 조회/페이징 처리/TDD) ]

* [ SpringBoot JPA 게시판 CRUD(Update-TDD) ]

* [ SpringBoot JPA 게시판 CRUD(Delete-TDD) ]

* [ SpringBoot JPA 게시판 CRUD(예외처리) ]

반응형