[Springboot] 7 Test 코드 작성법

김호정's avatar
Aug 16, 2024
[Springboot] 7 Test 코드 작성법
 
💡
test 폴더 → shop.mtcoding.blog → board 패키지 생성 → BoardRepositoryTest 파일 만들어주기
notion image
테스트 클래스 제목과 테스트 메서드 제목은 컨벤션을 지켜서 작성해준다.
notion image
package shop.mtcoding.blog.board; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; //@SpringBootTest // C R e h2 -> 모든 레이어를 메모리에 다 올리고 테스트할 때 사용하는 어노테이션 @DataJpaTest // h2, em @Import(BoardRepository.class) // boardRepository public class BoardRepositoryTest { // 클래스명은 Test 붙여서 짓는다 (컨벤션) @Autowired // IoC에 있는거 가져옴 private BoardRepository boardRepository; // 테스트 매서드에서는 매개변수를 사용할 수 없다.(save_test(int a) 이렇게 못함) // 매서드명_test : 컨벤션 @Test public void save_test() { // 매서드명_test 로 매서드 이름을 짓는다 (컨벤션) // given (매개변수를 강제로 만들기) String title = "제목1"; String content = "내용1"; // when boardRepository.save(title, content); // eye(눈으로 확인) } }
 
  • 클래스명은 테스트할 클래스명에 Test를 붙여서 짓는다 (컨벤션) ex) BoardRepositoryTest
  • 매서드명은 테스트할 매서드명에 test로 매서드 이름을 짓는다 (컨벤션) ex) save_test
 
  • @SpringBootTest 는 모든 레이어를 메모리에 다 올리고 테스트할 때 사용
  • 현재 이 테스트에서는 Repository ~ DB 를 테스트 할 거니 @DataJpaTest 와 @Import
    • 어노테이션을 사용한다.
       
      @DataJpaTest 를 사용하면 데이터베이스와 entityManager 를 테스트 할 수 있게되고,
      @Import(BoardRepository.class)를 사용하면 BoardRepository 를 테스트 할 수 있게된다.
       
       
  • given 영역 → 매개변수 생성
  • when → 테스트 할 거 적기
  • eye → 눈으로 확인
 
 
View
Form 의 name 은 버튼을 클릭하면, 파라미터로 넘어간다.
이때 name에 적어준 이름이랑 컨트롤러에서 매개변수로 받는 이름이랑 동일해야 한다.
불일치하면 null 값이 나오게 된다.
 
notion image
notion image
 
💡
form 태그에 action, method, enctype 을 입력해준다. form에 enctype을 application/x-www-form-urlencoded 로 설정해주면 데이터가 key=value&key=value 이런 형태로 넘어간다. 예 ) title의 input에 제목1을 입력하고, content의 textarea에 내용1을 입력하면, title=제목1&content=내용1 이런 형태로 데이터가 submit 되는 것이다.
 
notion image
매개변수 이름과 name 이 동일해야 한다.
 
package shop.mtcoding.blog.board; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; // 책임 : 식별자 요청 받기 & 응답하기 // 컨트롤러는 REPOSITORY에 의존하고 있다. (Repository가 없으면 일을 못하기 때문에) @Controller // 식별자 요청을 받을 수 있다. public class BoardController { /* private final BoardRepository boardRepository; public BoardController(BoardRepository boardRepository) { System.out.println("BoardController 생성자"); this.boardRepository = boardRepository; } */ @Autowired private BoardRepository boardRepository; @PostMapping("/board/save") public String save(String subtitle, String postContent) { // 스프링 기본전략 x-www-encoded // requestGetParameter안해도 이름이 폼태그의 name과 동일하면 자동으로 받아진다. boardRepository.save(subtitle, postContent); return "redirect:/board"; //redirect는 다른 컨트롤러를 때리는 것 } // get, post, put, delete // insert, delete, update 할때 일단 지금은 post 를 쓰자 @GetMapping("/board") // 식별자 요청 public String list() { return "board/list"; // return의 위치가 templates 폴더로 고정되어 있다. / 가 없어도 바로 templates 폴더로 연결된다. // 확장자도 mustache 로 자동설정 되어있기 때문에 파일명만 적어주면 거기로 리턴한다. } // 1. 매서드 : Get // 2. 주소 : /board/1 // 3. 응답 : board/detail @GetMapping("/board/1") public String detail() { return "board/detail"; } // 1. 매서드 : Get // 2. 주소 : /board/save-form // 3. 응답 : board/save-form @GetMapping("/board/save-form") public String saveForm() { return "board/save-form"; } // 1. 매서드 : Get // 2. 주소 : /board/1/update-form // 3. 응답 : board/update-form @GetMapping("/board/1/update-form") public String updateForm() { return "board/update-form"; } }
 
💡
Tip) 재실행하기 전에 콘솔에 뭐가 많으면 확인하기가 어려우니 실행 전, console → 마우스 우클릭 → Clear All 을 해준다.
notion image
 
Clear All을 해주면 아래와 같이 깔-끔 해진다 :)
notion image
 

쿼리부분 작성할 때 조심해야 할 점!

아래부분에 왜 에러가 떴나?
 
쿼리에 post_content라고 해야하는데, postContent 라고 했기 때문에
(쿼리는 데이터베이스에서 사용하는건데 자바랑 동일하지 않아서 자바의 카멜표기법 이런거 모름)
 
notion image
 
위의 코드를 실행시키니 에러가 난다.
왜? 쿼리부분에 자바문법이 들어갔기 때문이다.
대문자를 테이블에서는 C → _c 이런 식으로 입력해야 인식을 한다.
그런데 위에서는 postContent 라고 자바만 알아먹을 수 있는 형태로 쿼리문을 썼다.
그래서 post_content 만 아는 DB는 postContent 라는 건 없는데? 하면서
SQLGrammerException 에러를 발생시킨다.
 
notion image
 
에러 부분을 자세히 보면 하이버네이트가 실행되면서
테이블을 생성하는데, Create table post_tb (id integer, created_at timestamp, post_content varchar(255)…) 이렇게 테이블을 생성한다.
 
그리고 데이터 베이스에 보면 대문자부분이 _ 처리된 걸 확인할 수 있다.
notion image
 
이렇게 테이블의 column명을 지어놓았는데
repository의 쿼리가 postContent 라고 부르면 그게 누군데? 이렇게 된다.
 
postContent X
post_content O
 
그러니 쿼리를 작성할 때는 자바가 아닌 데이터베이스가 알아먹을 수 있게 써줘야 한다.
 

디버깅 하는 방법

DB 쪽에 에러가 났는지, 컨트롤러 쪽에 에러가 났는지 확인해야 할 때
단위테스트가 잘 되어 있으면 어디서 에러가 났는지 확인하기가 수월하다.
 
테스트할 때,,
만약 해야 할 일이 Table명을 board_tb → post_tb로 바꾸는 것, Column명을 title → subtitle로 바꾸는 것, Column명을 content → postContent 로 바꾸는 것이었다면,
한번에 하고 테스트하지 말고, 하나씩 수정하고 테스트를 해서 잘 되는지 체크하는게
디버깅하기 수월하다.
 
notion image
 
Board 엔티티의 속성? 이름을 title → subtitle, content → postContent로 바꾸어 주었다.
(테이블 이름도 board_tb 에서 post_db 로 변경했다)
 
이름이 바뀌었으니
notion image
폼의 name도 바꾸어 주어야 한다.
엔티티에서 바꾼 이름과 동일하게 name도 바꿔주고,
Repository의 쿼리문도 테이블명, 컬럼명을 바꿔줘야 한다.
notion image
 
컨트롤러에서 name을 받는 매개변수명도 기존의 title, content에서 변경한 subtitle, postContent로
변경해주어야 한다.
아래와 같이 변경하지 않은 채로 두면 name과 연결이 안되어서 둘 다 null 값으로 나온다.
notion image
 
notion image
위와 같이 Null not allowed for column “POST_CONTENT” 가 나온건
컨트롤러에서 form 값을 제대로 못받아서 null 값이 넘어갔기 때문에 null 에러가 뜬 것이다.
(→ title, content 둘 다 name과 매칭되는 이름으로 수정하지 않았기 때문에 둘 다 null 값이 들어가서 에러가 남)
 
+)
그리고 form name이랑 controller의 매개변수명이 일치하지 않아서 null 값이 들어갈 때,
왜 title에서 먼저 터지지 않고, 아래와 같이 postContent에서 null 에러가 뜬건지 궁금해서
선생님한테 물어봤는데
→ 통신을 받을때는 우리가 작성한 순서대로 넘어가지 않는다.
아래와 같이 썼다고, subtitle, post_content로 넘어가지 않는다.
뒤죽박죽으로 가서 post_content 가 먼저 인식되어서
subtitle과 post_content 둘 다 값에 문제가 있는데(null 허용안하는데 null 값이 들어가 있다든가)
post_content에서 에러가 먼저 터질 수 있다.
notion image
 
그래서 일단 둘다 문제가 있지만 아래처럼 에러가 발생하는 것이다.
notion image
 
 
 
Share article

keepgoing