📌 TDD (Test-Driven Development)
테스트 코드를 먼저 작성하고 실제 동작하는 코드를 개발하는 순서로 개발하는 개발 방법론
설계 → 개발 → 테스트 에서 설계 → 테스트 → 개발 순서로!
📌 Given - When - Then Pattern
테스트 코드를 작성하는 가장 대표적인 방법론
Given - 준비 When - 실행 Then - 검증 이렇게 단계별로 테스트코드를 나누어서, 매우 직관적으로 작성 할 수 있습니다.
💡 JUnit
자바 프로그래밍 언어 용 단위 테스트 프레임워크
프로그램을 작은 단위로 쪼개서 각 단위가 정확하게 동작하는지 검사하고 이를 통해 문제 발생 시 정확하게 어느 부분이 잘못되었는지를 재빨리 확인할 수 있게 해준다.
- build.gradle 파일에 JUnit 사용을 위한 환경설정이 이미 되어있음
- 테스트 파일 생성
테스트 파일을 생성할 파일내에서 Alt + Insert 후 Test 클릭
OK를 클릭하여 Test 파일을 생성해줍니다.
- 어노테이션
생성한 클래스에 어노테이션 추가
@Nested // 테스트 코드를 작성하고 확인을 했을 때 눈에 잘 보일 수 있게 계층구조로 보여줌
@DisplayName("회원이 요청한 관심상품 객체 생성") // 설정대로 보임
class CreateUserProduct {
...
}
기본값 설정 메서드에 어노테이션 추가
@BeforeEach // 테스트 하는데 필요한 부분을 미리 설정
void setup() {
...
}
테스트할 메서드에 어노테이션 추가
@Test // 테스트 할 메서드
@DisplayName("정상 케이스") // 설정대로 보임
void createProduct_Normal() {
...
assertNull(product.getId());
assertEquals(userId, product.getUserId());
}
Unit에서 제공해주는 검증 함수
asserNull : 주어진 인자(product.getId())가 null이어야 테스트를 통과시켜주는 함수
assertEquals : 주어진 인자 두개가 같아야 테스트를 통과시켜주는 함수
실패 케이스
@Test
@DisplayName("실패 케이스 null")
void fail1() {
...
Exception exception = assertThrows(IllegalArgumentException.class, () -> { // 예상하는 예외처리를 설정
new Product(requestDto, userId);
});
assertEquals("저장할 수 있는 상품명이 없습니다.", exception.getMessage()); // 우리가 설정한 메세지와도 같아야함
}
예시
class ProductTest {
@Nested
@DisplayName("회원이 요청한 관심상품 객체 생성")
class CreateUserProduct {
private Long userId;
private String title;
private String image;
private String link;
private int lprice;
@BeforeEach
void setup() {
userId = 100L;
title = "오리온 꼬북칩 초코츄러스맛 160g";
image = "https://shopping-phinf.pstatic.net/main_2416122/24161228524.20200915151118.jpg";
link = "https://search.shopping.naver.com/gate.nhn?id=24161228524";
lprice = 2350;
}
@Test
@DisplayName("정상 케이스")
void createProduct_Normal() {
// given
ProductRequestDto requestDto = new ProductRequestDto(
title,
image,
link,
lprice
);
// when
Product product = new Product(requestDto, userId);
// then
assertNull(product.getId());
assertEquals(userId, product.getUserId());
assertEquals(title, product.getTitle());
assertEquals(image, product.getImage());
assertEquals(link, product.getLink());
assertEquals(lprice, product.getLprice());
assertEquals(0, product.getMyprice());
}
@Nested
@DisplayName("실패 케이스")
class FailCases {
@Nested
@DisplayName("회원 Id")
class userId {
@Test
@DisplayName("null")
void fail1() {
// given
userId = null;
ProductRequestDto requestDto = new ProductRequestDto(
title,
image,
link,
lprice
);
// when
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
new Product(requestDto, userId);
});
// then
assertEquals("회원 Id 가 유효하지 않습니다.", exception.getMessage()); // 우리가 설정한 메세지와도 같아야함
}
@Test
@DisplayName("마이너스")
void fail2() {
// given
userId = -100L;
ProductRequestDto requestDto = new ProductRequestDto(
title,
image,
link,
lprice
);
// when
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
new Product(requestDto, userId);
});
// then
assertEquals("회원 Id 가 유효하지 않습니다.", exception.getMessage());
}
}
}
}
}
왼쪽 재생 버튼을 클릭하여 전체 혹은 각 메서드 별로 테스트 진행이 가능하다.
실행하면 아래 계층구조로 결과를 확인할 수 있다.
Mockito mock 을 사용한 단위 테스트
- 실제 객체와 겉만 같은 객체. 동일한 클래스명, 함수명
- 실제 DB 작업은 하지 않음
- mock 객체를 반환하여 서비스로직만을 배타적으로 테스트 가능
일단 먼저 그래들 dependencies에 추가해줍니다.
// 테스트 케이스를 위해 가짜 객체(Mock object)를 생성
testImplementation 'org.mockito:mockito-core:4.8.0'
testImplementation 'org.mockito:mockito-junit-jupiter:4.8.0'
class ProductServiceTest {
@Mock // 모킹할 객체 표기
ProductRepository productRepository;
@InjectMocks // 모킹한 객체 주입 (가짜 객체가 들어감)
ProductService productService;
@Test
@DisplayName("관심 상품 희망가 - 최저가 이상으로 변경")
void updateProduct_Success() {
...
// 가짜 객체를 만들어 서비스 로직만 테스트 가능
ProductRequestDto requestProductDto = new ProductRequestDto( ... );
when(productRepository.findByIdAndUserId(productId, userId)) // 모킹한 객체가 특정 조건으로 호출되면
.thenReturn(Optional.of(product)); // 일괄적으로 다음과 같이 동작하도록 지정
assertDoesNotThrow( () -> {
productService.updateProduct(productId, requestMyPriceDto, user);
});
}
}
'개발일기 > Java' 카테고리의 다른 글
커스텀 한 예외를 글로벌로 예외 처리하기 (0) | 2023.03.17 |
---|---|
Spring Security 사용 시 Swagger 설정 (0) | 2023.03.15 |
PostMan jwt 토큰 한 번에 설정하기 (0) | 2023.03.11 |
Spring Security 프로젝트 설정 (0) | 2023.03.11 |
스프링부트 Swagger 사용법 (0) | 2023.03.09 |