- 테스트: 프로그램의 품질을 검증하는 것으로, 의도대로 프로그램이 잘 동작하는지 확인 하는 과정
- 테스트 도구를 이용해 반복적인 검증 절차를 자동화 가능
- 다양한 문제를 미리 예방 하고 코드 변경 등으로 인해 발생하는 부작용도 조기 발견 가능
- 테스트가 통과하면 지속적인 리팩터링으로 코드를 개선
- 테스트가 실패하면 잘못된 부분을 찾아 고치는 디버깅을 함
- 테스트 코드 작성 단계
- 예상 데이터 작성하기
- 실제 데이터 획득하기
- 예상 데이터와 실제 데이터 비교해 검증하기
- 테스트 주도 개발(TDD, Test Driven Development)
- 먼저 테스트 코드를 만든후 이를 통과하는 최소한의 코드부터 시작해 점진적으로 코드를 개선 및 확장해 나가는 개발 방식
테스트 코드 작성하기
- 테스트 코드 기본 틀 만들기
- 프로젝트의 src/test/java 디렉터리 안에 service 패키지를 생성후 패키지 안에 ArticleServiceTest.java 파일 생성
- 테스트할 메서드 위에 @Test 어노테이션 작성
- @Test 은 해당 메서드가 테스트를 위한 코드라고 선언
- 노란줄이 있는 이유는 사용되는 곳이 없다는 의미
- 테스트 코드를 스프링 부트와 연동하기 위해 다음과 같이 설정
- ArticleServiceTest 클래스 위에 @SpringBootTest 작성
- @SpringBootTest는 스프링 부트와 연동해 통합 테스트를 수행하겠다고 선언
- @SpringBootTest을 사용하면 테스트 코드에서 스프링 부트가 관리하는 다양한 객체를 주입 가능
- ArticleServiceTest 클래스를 테스트하기 위해 articleService 객체를 선언하고 외부에서 객체를 주입해야 하므로 @Autowired 도 작성
- ArticleServiceTest 클래스 위에 @SpringBootTest 작성
package com.example.firstproject.service;
import org.junit.jupiter.api.Test; //Test 패키지 임포트
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*; // 앞으로 사용할 수 있는 패키지 임포트
@SpringBootTest // 해당 클래스를 스프링 부트와 연동해 테스트
public class ArticleServiceTest {
@Autowired
ArticleService articleService; // 객체 주입
@Test //해당 메서드가 테스트 코드임을 선언
void index() {
}
}
index() 테스트하기
- index()를 테스트하는 순서
- 예상 데이터 작성하기
- 데이터를 Article 객체에 a, b, c에 저장 이때 id 는 Long 타입으로 접미사 L을 붙임
- a, b, c 3개의 객체를 Arrays.asList() 메서드를 사용해 ArrayList을 합치고 이를 List<Article>타입의 expected에 저장
-
더보기Arrays.asList() 메서드 입력된 배열 또는 2개 이상의 동일한 타입 데이터를 정적 리스트로 만들어 반환
정적 리스트는 고정 크기이므로 add() 나 remove() 메서드를 사용할 수 없음
-
- 실제 데이터 획득하기
- articleService.index() 메서드를 호출해 그 결과를 List<<Article>타입의 articles 에 받아옴
- 예상 데이터와 실제 데이터 비교해 검증하기
- assertEquals(x,y) 메서드를 이용
- JUnit에서 제공하는 메서드
- 예상 데이터(x) ,예상 데이터(y)를 비교해 일치하면 테스트를 통과
- x에는 예상 데이터를 문자열로 변환한 expected.toString()을 넣고 y에는 실제 데이터를 문자열로 변환한 articles.toString()을 넣음
- assertEquals(x,y) 메서드를 이용
- 테스트 코드를 실행 하면 테스트 통과 실패를 알수 있음
- 예상 데이터 작성하기
@SpringBootTest // 해당 클래스를 스프링 부트와 연동해 테스트
public class ArticleServiceTest {
@Autowired
ArticleService articleService; // 객체 주입
@Test //해당 메서드가 테스트 코드임을 선언
void index() {
// 1. 예상 데이터
Article a = new Article(1L , "가가가가" , "1111"); //예상 데이터 객체로 저장
Article b = new Article(2L , "나나나나" , "2222"); //예상 데이터 객체로 저장
Article c = new Article(3L , "다다다다" , "3333"); //예상 데이터 객체로 저장
List<Article> expected = new ArrayList<Article>(Arrays.asList(a,b,c)); // a,b,c 합치기
// 2. 실제 데이터
List<Article> articles = articleService.index();
// 3. 비교 및 검증
assertEquals(expected.toString(), articles.toString());
}
}
show() 테스트하기
- 게시글을 조회하는 show() 메서드를 테스트 하기위해 ArticleServiceTest 코드 맨 아래에 show()테스트 코드 추가
- 게시글 조회에 성공하는 경우와 실패하는 경우로 나눠서 테스트 케이스를 작성하기 위해 show()테스트 코드를 복사하고 하나는 show_성공() , show_실패()로 수정
- 게시물 성공하는 상황으로는 존재하는 id를 입력해 성공하는 경우를 테스트
- show_성공() 메서드 이름을 show_성공_존재하는_id_입력()으로 수정
- 실제 데이터를 얻기 위해 articleService.show(id) 메서드를 호출해서 얻은 결과를 article 객체에 저장
- 실제 데이터와 예상 데이터를 assertEquals()메서드를 이용해 비교
- 게시글 조회에 실패하는 상황으로는 존재하는 id를 입력해 실패하는 경우로 테스트
- show_실패() 메서드 이름을 show_실패_존재하지_않는_id_입력()으로 수정
- 실제 데이터를 얻기 위해 articleService,show(id) 메서드를 호출해서 얻은 결과를 article 객체에 저장
- 예상 데이터는 존재하지 않는 id인 -1을 조회한다고 가정해 작성하므로 DB에서 조회되는 내용이 없어 null 을 반환할 것으므로 expected 객체에 null을 저장
- 비교 및 검증은 assertEquals() 메서드를 이용하지만 실제 데이터와 예상 데이터의 값 null은 toString() 메서드를 호출할 수 없으므로 첫 번째와 두 번째 전달값은 expected와 article을 사용
- 실패 테스트를 실행하면 존재하지 않는 id로 조회했을 때 실제 null을 반환했고 예상 데이터에서도 null을 반환했기 때문에 테스트 통과
@Test
void show_성공_존재하는_id_입력() {
// 1. 예상 데이터
Long id = 1L; //예상 데이터 저장
Article expected = new Article(id,"가가가가","1111"); //예상 데이터 저장
// 2. 실제 데이터
Article article = articleService.show(id); //실제 데이터 저장
// 3. 비교 및 검증
assertEquals(expected.toString(),article.toString() ); //비교
}
@Test
void show_실패_존재하는_id_입력() {
// 1. 예상 데이터
Long id = -1L; //예상 데이터 (null)저장
Article expected = null; //예상 데이터(null) 저장
// 2. 실제 데이터
Article article = articleService.show(id); //실제 데이터 저장
// 3. 비교 및 검증
assertEquals(expected,article); //비교
}
create() 테스트하기
- ArticleServiceTest 코드 맨 아래에 create() 테스트 코드 추가
- 게시글 생성에 성공하는 경우와 실패하는 경우로 나눠서 테스트 케이스를 작성하기 위해 create() 테스트 코드를 복사하고 하나는 create_성공() , create_ 실패()로 수정
- 게시글 생성에 성공하는 상황은 title 과 content만 있는 dto를 입력한 경우
- create_성공() 메서드 이름을 create_성공_title과_content만_있는_dto입력() 으로 수정
- 실제 데이터를 얻기 위해 articleService.create(dto)메서드를 호출에 얻은 결과를 article 객체에 저장
- 예상 데이터는 사용자가 새 게시물을 생성한 상황을 가정 그런데 id는 DB에서 자동으로 생성되므로 게시물의 내용으로 들어갈 title , content 필드만 선언하고 이를 dto 객체로 생성 그리고 expected 객체에 예상 데이터의 id, title, content 를 저장하는데 id는 필드를 따로 선언하지 않았으므로 자동으로 생성될 4L을 써줌
- 실제 데이터를 예상 데이터를 assertEquals() 메서드를 이용해 비교
- 게시글 생성에 실패하는 상황은 id가 포함된 dto가 입력된 경우
- create_실패() 메서드의 이름을 create_실패_id가_포함된_dto_입력()으로 수정
- 실제 데이터를 얻기 위해 articleService.create(dto) 메서드를 호출해 얻은 결과를 article 객체에 저장
- 예상 데이터에 id = 4L , title ="라라라라" , content ="4444" 필드를 선언하고 dto 객체로 생성
- 이렇게 게시글을 생성할떄 id를 넣으면 오류가 발생하기 때문에 null이 반환되므로 expected객체에 null을 저장
- 비교는 assertEquals() 메서드를 이용하지만 실제 데이터와 예상 데이터의 값 null은 toString() 메서드를 호출할수 없으므로 첫번째와 두번쨰 전달값은 expeted , article을 사용
@Test
void create_성공_title과_content만_있는_dto_입력() {
// 1. 예상 데이터
String title = "라라라라"; //title 값 임의 배정
String content = "4444"; // content 값 임의 배정
ArticleForm dto = new ArticleForm(null , title , content); //dto 생성
Article expected = new Article(4L , title , content); // 예상 데이터 저장
// 2. 실제 데이터
Article article = articleService.create(dto); //실제 데이터 저장
// 3. 비교 및 검증
assertEquals(expected.toString(), article,toString()); //비교
}
@Test
void create_실패_id가_포함된_dto_입력() {
// 1. 예상 데이터
Long id = 4L; //id 값 임의 배정
String title = "라라라라"; //title 값 임의 배정
String content = "4444"; // content 값 임의 배정
ArticleForm dto = new ArticleForm(id , title , content); //dto 생성
Article expected = new Article(4L , title , content); // 예상 데이터 저장
// 2. 실제 데이터
Article article = articleService.create(dto); //실제 데이터 저장
// 3. 비교 및 검증
assertEquals(expected.toString(), article,toString()); //비교
}
여러 테스트 케이스 한 번에 실행하기
- ArticleServiceTest 클래스에서 Run을 선택하면 앞에서 하나씩 테스트 할 때는 통과 하였지만 index()가 테스트 통과하지 못함
- 이러한 에러가 발생한 이유는 롤백하지 않아서 생긴 현상
- create() 메서드를 테스트할때 insert문을 실행해 새로운 데이터가 추가되서 발생한 현상
- 데이터를 조회(Read)하는 테스트를 제외하고 데이터를 생성(Create), 수정(Update), 삭제(Delete)하는 테스트를 할때는 반드시 해당 테스트를 트랜잭션으로 묶어 테스트가 종류한 후에 원래대로 돌아갈수 있도록 롤백 처리하기 위해 메서드 위에 @Transactional 을 추가
※ update , delete 테스트 코드 작성
@Test
@Transactional
void update_성공_존재하는_id와_title_content가_있는_dto_입력() {
// 1. 예상 데이터
Long id = 1L; //id 값 임의 배정
String title = "가나다라"; //title 값 임의 배정
String content = "1234"; // content 값 임의 배정
ArticleForm dto = new ArticleForm(id , title , content); //dto 생성
Article expected = new Article(id , title , content); // 예상 데이터 저장
// 2. 실제 데이터
Article article = articleService.update(id, dto); //실제 데이터 저장
// 3. 비교 및 검증
assertEquals(expected.toString(), article.toString()); //비교
}
@Test
@Transactional
void update_성공_존재하는_id와_title만_있는_dto_입력() {
// 1. 예상 데이터
Long id = 1L; //id 값 임의 배정
String title = "AAAA"; //title 값 임의 배정
String content = null; // content 값 임의 배정
ArticleForm dto = new ArticleForm(id , title , content); //dto 생성
Article expected = new Article(1L , "AAAA" , "1111"); // 예상 데이터 저장
// 2. 실제 데이터
Article article = articleService.update(id, dto); //실제 데이터 저장
// 3. 비교 및 검증
assertEquals(expected.toString(), article.toString()); //비교
}
@Test
@Transactional
void update_실패_존재하는_않는_id의_dto_입력() {
// 1. 예상 데이터
Long id = -1L; //id 값 임의 배정
String title = "가나다라"; //title 값 임의 배정
String content = "1234"; // content 값 임의 배정
ArticleForm dto = new ArticleForm(id , title , content); //dto 생성
Article expected = null; // 예상 데이터 저장
// 2. 실제 데이터
Article article = articleService.update(id, dto); //실제 데이터 저장
// 3. 비교 및 검증
assertEquals(expected, article); //비교
}
@Test
@Transactional
void delete_성공_존재하는_id_입력() {
// 1. 예상 데이터
Long id = 1L; //id 값 임의 배정
Article expected = new Article(id , "가가가가" , "1111"); //예상 데이터 저장
// 2. 실제 데이터
Article article = articleService.delete(id); //실제 데이터 저장
// 3. 비교 및 검증
assertEquals(expected.toString(), article.toString()); //비교
}
@Test
@Transactional
void delete_실패_존재하지_않는_id_입력() {
// 1. 예상 데이터
Long id = -1L; //id 값 임의 배정
Article expected = null; //예상 데이터 저장
// 2. 실제 데이터
Article article = articleService.delete(id); //실제 데이터 저장
// 3. 비교 및 검증
assertEquals(expected, article); //비교
}
'BE > 스프링 부트 3' 카테고리의 다른 글
15장 댓글 컨트롤러와 서비스 만들기 (2) | 2023.12.26 |
---|---|
14장 댓글 엔티티와 리파지터리 만들기 (1) | 2023.12.21 |
12장 서비스 계층과 트랜잭션 (0) | 2023.12.16 |
11장 REST API의 동작 이해하기 (0) | 2023.12.13 |
10장 REST API 와 JSON (0) | 2023.12.10 |