반응형
도서명 | 쪽수 | 저자, 출판사 | 발행일 | |||||
테스트 주도 개발 시작하기 : Test-Driven Development |
303 | 최범균, 가메출판사 | 2020년 02월 18일 |
1. TDD 개발 준비
- Test-Driven Development = 테스트 주도 개발
- TDD는 테스트부터 시작한다. 구현을 먼저 하고 나중에 테스트하는 것이 아니라 먼저 테스트를 하고 그다음에 구현한다.
기능을 검증하는 테스트 코드를 먼저 작성하고 테스트를 통과시키기 위해 개발을 진행
- TDD는 테스트부터 시작한다. 구현을 먼저 하고 나중에 테스트하는 것이 아니라 먼저 테스트를 하고 그다음에 구현한다.
- IntelliJ , Gradle에서 TDD 설정 방법
- Project SDK 에서 JDK의 버전은 자바 8 이상으로 선택
- Libraries 추가
File -> Project Structure - Gradle JUnit 설정
build.gradle 에 아래 내용 추가
-> gradle 4.6 버전부터 JUnit 5 플랫폼을 지원한다. 그 이전 버전일 경우 JUnit 4 버전을 사용하거나, gradle 버전을 올려야 한다.
-> junit-jupiter 모듈은 JUnit 5.4 버전부터 제공한다.
-> 버전별 메이븐/그레이들 설정 확인 : https://junit.org/junit5/docs/버전입력(5.10.2)/user-guide
dependencies {
.....
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
}
tasks.named('test') {
useJUnitPlatform()
}
2. TDD 시작
- TDD 흐름
- 테스트 -> 코딩 -> 리팩토링 -> 테스트..
- TDD는 테스트를 먼저 작성하고 테스트를 통과시킬 만큼 코드를 작성하고 리팩토링으로 마무리하는 과정을 반복한다.
- TDD 작성 방법
- test class 생성 (보통 다른 클래스와 구분을 쉽게 하기 위해 'Test'를 접미사로 붙인다.)
- 메서드에 @Test 어노테이션을 붙이면 테스트 메서드로 인식한다. (해당 메서드는 private이면 안된다.)
- 테스트를 실행하는 메서드는 JUnit이 제공하는 검증 메서드를 이용해서 결과를 확인한다. ex. assertEquals()
- TDD는 테스트를 먼저 작성하고 테스트에 실패하면 테스트를 통과시킬 만큼 코드를 추가하는 과정을 반복하면서 점진적으로 기능을 완성해 나간다.
- TDD 특징
- 테스트가 개발을 주도한다. 테스트 코드가 추가되면서 검증하는 범위가 넓어질수록 구현도 점점 완성되어간다.
- 리팩토링을 통한 지속적인 코드 정리
- 코드 수정에 대한 빠른 피드백 가능, 잘못된 코드가 배포되는 것을 방지한다.
3. 테스트 코드 작성 순서
- 쉬운 경우에서 어려운 경우로 진행
- 예외적인 경우에서 정상인 경우로 진행
- 초반에 복잡한 테스트부터 시작하면 안 되는 이유
-> 초반부터 다양한 조합을 검사하는 상황을 테스트로 추가하면 해당 테스트를 통과시키기 위해 한 번에 구현해야 할 코드가 많아진다. - 구현하기 쉬운 테스트부터 시작하기
-> 가장 구현하기 쉬운 경우부터 시작하면 빠르게 테스트를 통과시킬 수 있다.
ex) 모든 조건을 충족하는 경우 > 한 규칙만 충족하는 경우 > 모든 조건을 충족하지 않는 경우 - 예외 상황을 먼저 테스트해야 하는 이유
-> 초반에 예외 상황을 테스트하면, 예외 상황에 따른 if-else 구조가 미리 만들어지기 때문에 많은 코드를 완성한 뒤에 예외 상황을 반영할 때보다 코드 구조가 덜 바뀐다. - 완급 조절 - 한 번에 얼마만큼의 코드를 작성할 것인지 아래 단계에 따라 TDD를 익혀보기
1. 정해진 값을 리턴
2. 값 비교를 이용해서 정해진 값을 리턴
3. 다양한 테스트를 추가하면서 구현을 일반화 - 지속적인 리팩토링
-> 테스트를 통과한 뒤에는 리팩토링을 진행한다. 코드 중복은 대표적인 리팩토링 대상이다.
TDD를 진행하는 과정에서 지속적으로 리팩토링을 진행하면 코드 가독성이 높아진다.
코드 가독성이 높아지면 개발자는 더욱 빠르게 코드를 분석할 수 있어 수정 요청이 있을 때 변경할 코드를 빠르게 찾을 수 있다.
코드 변경의 어려움을 줄여주어 향후 유지보수에 도움이 된다.
상수 -> 변수 변경, 변수 이름 변경 등 작은 리팩토링은 발견하면 바로 실행하고,
메서드 추출과 같이 메서드의 구조에 영향을 주는 리팩토링은 큰 틀에서 구현 흐름이 눈에 들어오기 시작한 뒤에 진행한다.
파라미터 개수는 적을수록 코드 가독성과 유지보수에 유리하므로 메서드의 파라미터 개수가 세 개 이상이면 객체로 바꿔 한 개로 줄이는 것을 고려해야 한다.
범위가 큰 리팩토링을 진행하기 전에는 코드를 커밋하거나, 별도 브랜치에서 리팩토링을 진행하는 것이 좋다.
(실패했을 때 다시 동작하는 마지막 상태로 쉽게 돌아올 수 있다.) - 테스트할 목록 정리하기
TDD를 시작할 때 테스트할 목록을 미리 정리하면 좋다.
정리했다면 그중에 어떤 테스트가 구현이 쉬울지, 또는 어떤 테스트가 예외적인지 상상해 본다.
테스트 과정에서 새로운 테스트 사례를 발견하면 그 사례를 목록에 추가해서 놓치지 않도록 해야 한다.
4. TDD, 기능 명세, 설계
- 설계 과정을 지원하는 TDD
- TDD 자체가 설계는 아니지만, TDD를 하다 보면 테스트 코드를 작성하는 과정에서 일부 설계를 진행하게 된다.
- 설계 과정에서 구현하는 기능을 정확하게 표현하는 이름을 사용하는 것만큼 중요한 것은 없다. 이름을 고민하는 시간을 아까워하지 말자.
- 필요한 만큼 설계하기
- TDD는 테스트를 통과할 만큼만 코드를 작성한다. 필요한 만큼 설계하기. 필요할 것으로 예측해서 미리 설계를 유연하게 만들지 않는다.
- 기능 명세 구체화
- 테스트 코드를 작성하려면 파라미터와 결과 값을 정해야 하므로 개발자는 요구사항 문서에서 기능의 입력과 결과를 도출해야 한다.
- 테스트 코드를 작성하려면 입력과 결과가 명확해야 하므로 애매한 점을 발견하면 기획자나 실무 담당자와 얘기해서 상황에 따라 기능이 어떻게 동작해야 하는지 구체적으로 정리해야 한다.
- 구체적인 예를 이용해서 테스트 코드를 추가하다 보면 기능 명세를 보다 잘 이해하고 모호함을 없앨 수 있다. good
5. JUnit 5 기초
- JUnit 플랫폼 : 테스팅 프레임워크를 구동하기 위한 런처와 테스트 엔진을 위한 API를 제공한다.
- JUnit 주피터(Jupiter) : JUnit 5를 위한 테스트 API와 실행 엔진을 제공한다.
- JUnit 빈티지(Vintage) : JUnit 3과 4로 작성된 테스트를 JUnit 5 플랫폼에서 실행하기 위한 모듈을 제공한다.
- 주요 단언 메서드
- assertEquals(expected, actual) : 실제 값(actual)이 기대하는 값(expected)과 같은지 검사한다.
- assertNotEquals(unexpected, actual) : 실제 값(actual)이 특정 값(unexpected)과 같지 않은지 검사한다.
- assertSame(Object expected, Object actual) : 두 객체가 동일한 객체인지 검사한다.
- assertNotSame(Object expected, Object actual) : 두 객체가 동일하지 않은 객체인지 검사한다.
- assertTrue(boolean condition) : 값이 true인지 검사한다.
- assertFalse(boolean condition) : 값이 false인지 검사한다.
- assertNull(Object actual) : 값이 null인지 검사한다.
- assertNotNull(Object actual) : 값이 null이 아닌지 검사한다.
- fail() : 테스트를 실패 처리한다.
- assertDoesNotThrow(Executable executable) : executable을 실행한 결과로 익셉션이 발생하지 않는지 검사한다.
- assertThrows(Class<T> expectedType, Executable executable) : executable을 실행한 결과로 지정한 타입의 익셉션이 발생하는지 검사한다.
@Test
void assertThrows() {
// 발생한 익셉션을 이용해서 추가로 검증이 필요할 경우 assertThrows() 메서드가 리턴한 익셉션 객체를 사용
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
() -> {
AuthService authService = AuthService();
authService.authenticate(null, null);
});
assertTrue(thrown.getMessage().contains("id"));
}
- assertAll()
assert 메서드는 실패하면 다음 코드를 실행하지 않고 바로 익셉션을 발생한다.
일단 모든 검증을 실행하고 그중에 실패한 것이 있는지 확인하고 싶을 때는 assertAll() 사용
assertAll(
() -> assertEquals(3, 5 / 2),
() -> assertEquals(4, 2 * 2),
() -> assertEquals(6, 11 / 2),
);
- 테스트 라이프사이클
- @BeforeEach : 테스트를 실행하는데 필요한 준비 작업을 할 때 사용 (임시 파일 생성 등)
- @AfterEach : 테스트를 실행한 후에 정리할 것이 있을 때 사용 (임시 파일 삭제 등)
/*
JUnit은 각 테스트 메서드마다 다음 순서대로 코드를 실행
1. 테스트 메서드를 포함한 객체 생성
2. (존재하면) @BeforeEach 어노테이션이 붙은 메서드 실행
3. @Test 어노테이션이 붙은 메서드 실행
4. (존재하면) @AfterEach 어노테이션이 붙은 메서드 실행
실행결과:
new LifecycleTest
setUp
A
tearDown
new LifecycleTest
setUp
B
tearDown
*/
package com.vennygo.study;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class LifeCycleTest {
public LifeCycleTest() {
System.out.println("new LifecycleTest");
}
@BeforeEach
void setUp() {
System.out.println("setUp");
}
@Test
void a() {
System.out.println("A");
}
@Test
void b() {
System.out.println("B");
}
@AfterEach
void tearDown() {
System.out.println("tearDown");
}
}
-
- @BeforeAll : 한 클래스의 모든 테스트 메서드를 실행하기 전에 한 번 실행된다. (정적 메서드에 붙임)
- @AfterAll : 클래스의 모든 테스트 메서드를 실행한 뒤에 실행된다. (정적 메서드에 붙임)
- 테스트 메서드 간 실행 순서 의존과 필드 공유하지 않기
-> 각 테스트 메서드는 서로 독립적으로 동작해야 한다. 한 테스트 메서드의 결과에 따라 다른 테스트 메서드의 실행 결과가 달라지면 안된다. - 추가 어노테이션
- @DisplayName : 메서드 이름만으로 테스트 내용을 설명하기가 부족할 때 사용
- @Disabled : 어노테이션이 붙은 클래스나 메서드는 테스트 실행 대상에서 제외 (미완성이거나 잠시 실행하지 말아야 할 때)
- @DisplayName : 메서드 이름만으로 테스트 내용을 설명하기가 부족할 때 사용
package com.vennygo.study;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DisplayName("@DisplayName, @Disabled 어노테이션 테스트")
public class AnnotationTest {
@DisplayName("메서드 설명")
@Test
void displayName() {
assertEquals(2, 1+1);
}
@Disabled
@Test
void disabled() {
assertEquals(3, 1+2);
}
}
- 모든 테스트 실행하기
- 코드를 원격 리포지토리에 푸시하거나 코드를 빌드해서 운영 환경에 배포하기 전에는 모든 테스트를 실행해서 깨지는 테스트가 없는지 확인한다.
src/test/java 폴더 우클릭 -> Run Tests
- 코드를 원격 리포지토리에 푸시하거나 코드를 빌드해서 운영 환경에 배포하기 전에는 모든 테스트를 실행해서 깨지는 테스트가 없는지 확인한다.
6. 테스트 코드의 구성
- 상황, 실행, 결과 확인
7. 대역
- 테스트 대상에서 의존하는 요인 때문에 테스트가 어려울 때는 대역을 써서 테스트를 진행할 수 있다.
외부 요인 예)
- 테스트 대상에서 파일 시스템을 사용
- 테스트 대상에서 DB로부터 데이터를 조회하거나 데이터를 추가
- 테스트 대상에서 외부의 HTTP 서버와 통신 - 대역의 종류 (대역 종류마다 쓰임새가 다름)
- 스텁(Stub) : 구현을 단순한 것으로 대체한다. 테스트에 맞게 단순히 원하는 동작을 수행한다.
- 가짜(Fake) : 제품에는 적합하지 않지만, 실제 동작하는 구현을 제공한다. ex) DB 대신에 메모리를 이용해서 구현
- 스파이(Spy) : 호출된 내역을 기록한다. 기록한 내용은 테스트 결과를 검증할 때 사용한다.
- 모의 객체(Mock) : 기대한 대로 상호작용하는지 행위를 검증한다. 기대한 대로 동작하지 않으면 익셉션을 발생할 수 있다. 모의 객체는 스텁이자 스파이도 된다.
- 대역과 개발 속도
- TDD 과정에서 대역을 사용하지 않고 실제 구현을 사용한다면 다음과 같은 일이 벌어지게 된다.
- 카드 정보 제공 업체에서 도난 카드번호를 받을 때까지 테스트를 기다린다.
- 카드 정보 제공 API 가 비정상 응답을 주는 상황을 테스트하기 위해 업체의 변경 대응을 기다린다.
- 회원 가입 테스트를 한 뒤에 편지가 도착할 때까지 메일함을 확인한다.
- 약한 암호 검사 기능을 개발할 때까지 회원 가입 테스트를 대기한다. - 즉, 대역은 의존하는 대상을 구현하지 않아도 테스트 대상을 완성할 수 있게 만들어주며, 이는 대기 시간을 줄여주어 개발 속도를 올리는 데 도움이 된다.
- 모의 객체는 스텁과 스파이를 지원하므로 대역으로 모의 객체를 많이 사용하는데,
모의 객체를 과하게 사용하면 오히려 테스트 코드가 복잡해지는 경우도 발생하므로 과하게 사용하지 않는다.
- TDD 과정에서 대역을 사용하지 않고 실제 구현을 사용한다면 다음과 같은 일이 벌어지게 된다.
8. 테스트 가능한 설계
- 테스트가 어려운 코드
- 하드 코딩된 경로
- 의존 객체를 직접 생성
public class PaySync { // 의존 대상을 직접 생성, 이 코드를 테스트 하려면 DB 테이블, 데이터 등 모든 환경을 구성해야 한다. private PayInfoDao payInfoDao = new PayInfoDao(); public void synce() throws IOException { payInfos.forEach(pi -> payInfoDao.insert(pi)); } }
- 정적 메서드 사용
- 실행 시점에 따라 달라지는 결과 (ex. LocalDate.now() , Random() 등)
- 역할이 섞여 있는 코드
- 테스트 가능한 설계로 교체하는 방법
- 하드 코딩된 상수를 생성자나 메서드 파라미터로 받기
- 의존 대상을 주입 받기 (생성자나 세터를 주입 수단으로 이용)
- 테스트하고 싶은 코드를 분리하기
- 시간이나 임의 값 생성 기능 분리하기
- 외부 라이브러리는 직접 사용하지 말고 감싸서 사용하기
9. 테스트 범위와 종류
- 기능 테스트 (Functional Testing) : 사용자 입장에서 시스템이 제공하는 기능이 올바르게 동작하는지 확인 (End to End Test)
- 통합 테스트 (Integration Testing) : 시스템의 각 구성 요소가 올바르게 연동되는지 확인한다.
- 단위 테스트 (Unit Testing) : 개별 코드나 컴포넌트가 기대한대로 동작하는지 확인한다.
- 외부 연동 테스트가 필요할 경우
- WireMock -> JSON/XML 응답, HTTPS 지원, 단독 실행 등 다양한 기능을 제공하므로 외부 연동 코드를 테스트할 때 유용하게 사용할 수 있다.
- TestRestTemplate -> 스프링 부트가 테스트 목적으로 제공하는 것으로서 내장 서버에 연결하는 RestTemplate이다.
10. 테스트 코드와 유지보수
테스트 코드는 그 자체로 코드이기 때문에 제품 코드와 동일하게 유지보수 대상이 된다. 테스트 코드를 유지보수하는 데 시간이 많이 들기 시작하면 점점 테스트 코드를 손보지 않아 실패하는 테스트가 증가하게 된다.
그러므로 테스트 코드 자체의 유지보수성이 좋아야 한다.
- 처음 TDD를 시도하는 개발자들이 빠지기 쉬운 실수와 주의 사항
- 변수나 필드를 사용해서 기댓값 표현하지 않기
- 두 개 이상을 검증하지 않기
- 정확하게 일치하는 값으로 모의 객체 설정하지 않기
- 과도하게 구현 검증하지 않기
- 셋업을 이용해서 중복된 상황을 설정하지 않기
- 통합 테스트에서 데이터 공유 주의하기
- 통합 테스트의 상황 설정을 위한 보조 클래스 사용하기
- 실행 환경이 다르다고 실패하지 않기
- 실행 시점이 다르다고 실패하지 않기
- 랜덤하게 실패하지 않기
- 필요하지 않은 값은 설정하지 않기
- 단위 테스트를 위한 객체 생성 보조 클래스
- 조건부로 검증하지 않기
- 통합 테스트는 필요하지 않은 범위까지 연동하지 않기
- 더 이상 쓸모 없는 테스트 코드
11. 마치며
- 테스트 코드는 안전장치가 되어 변경한 코드로 인해 소프트웨어가 비정상적으로 동작하는 것을 사전에 막아준다.
- 테스트 코드를 만든다고 완벽하게 버그를 없앨 수는 없지만, 테스트 코드가 다루는 범위가 넓을수록 버그가 발생할 가능성도 줄어들고 버그 수정도 더 쉬워진다.
- 처음엔 쉬운 것을 TDD로 시도해서 TDD에 익숙해지고 이후에 점진적으로 TDD 적용 범위를 늘려나가자.
부록 A. JUnit 5 추가 내용
- JUnit 5는 조건에 따라 테스트를 실행할지 여부를 결정하는 기능을 제공한다.
- @EnabledOnOS, @DisabledOnOs = 테스트 메서드가 특정 운영체제에서만 동작 or 비동작 해야할 때 사용
ex) @EnabledOnOs(OS.WINDOWS) - @EnabledOnJre, @DisalbedOnJre = 테스트 메서드가 특정 자바 버전에 따라 동작 or 비동작 해야할 때 사용
ex) @EnabledOnJre({JRE.JAVA_8, JRE.JAVA_9}) - @EnabledIfSystemProperty, @DisabledIfSystemProperty = 시스템 프로퍼티 값을 비교하여 테스트 실행 여부를 결정
ex) @EnabledIfSystemProperty(named = "java.vm.name", matches = ".*OpenJDK.*")
-> java.vm.name 시스템 프로퍼티 값이 OpenJDK를 포함하고 있으면 해당 테스트를 실행 - @EnabledIfEnvironmentVariable, @DisabledIfEnvironmentVariable = 환경 변수 값을 비교하여 테스트 실행 여부를 결정
- @Tag = 테스트에 태그를 달 때 사용
@Tag 어노테이션을 이용하면 메이븐이나 그레이들에서 실행할 테스트를 선택할 수 있다. - @Nested = 중첩 클래스에서 테스트 메서드를 추가할 수 있다.
- @assertEquals() 메서드의 세 번째 인자 = 테스트에 실패할 때 표시할 메시지로 사용
- @TempDir = 임시 폴더 생성, 테스트를 실행한 뒤에는 생성한 임시 폴더를 삭제한다. (파일 포함)
- @Timeout = 테스트 실행 시간 검증, 테스트가 일정 시간 내에 실행되는지 검증할 수 있다.
ex) @Timeout(1) -> 테스트 메서드가 1초 이내에 실행되는지 검증
@Timeout(value = 500, unit = TimeUnit.MILLISECONDS) -> 테스트 메서드가 0.5초 이내에 실행되는지 검증
부록 B. JUnit 4 기초
- JUnit 5는 자바 8이상의 버전을 필요로 하지만, JUnit 4는 자바 1.5 이상에서 동작한다.
- 기본 어노테이션은 @Before, @Test, @After 이다.
- JUnit 5와 달리 JUnit 4의 테스트 메서드는 public 이어야 한다.
- 주요 단언 메서드는 JUnit 5와 동일하지만, assertAll() 이나 assertThrows() 는 제공하지 않는다.
부록 C. Mockito 기초 사용법
- Mockito = 모의 객체 생성, 검증, 스텁을 지원하는 프레임워크
// gradle 추가
dependencits {
testImplementation 'org.mockito:mockito-core:2.26.0'
}
- Mockito.mock() = 클래스, 인터페이스, 추상 클래스에 대해 모의 객체를 생성할 수 있다.
- BDDMockito.given() = 모의 객체의 메서드가 특정 값을 리턴하도록 설정할 수 있다.
- ArgumentMatchers.any() = 정확하게 일치하는 값 대신 임의의 값에 일치하도록 설정할 수 있다.
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
public class GameGenMockTest {
@Test
void mockTest() {
GameNumGen genMock = mock(GameNumGen.class); // 1. 모의 객체 생성
given(genMock.generate(GameLevel.EASY)).willReturn("123"); // 2. 스텁 설정, gemMock.generate(GameLevel.EASY) 메서드가 불리면 "123"을 리턴하라고 설정
// willReturn 대신 willThrow 를 통해 익셉션을 발생하게 설정할 수도 있음
// given(genMock.generate(any())).willReturn("456"); // 모든 값이 456으로 리턴
String num = genMock.generate(GameLevel.EASY); // 3.스텁 설정에 매칭되는 메서드 실행, 인자로 GameLevel.EASY 를 전달하여 모의 객체의 generate() 메서드를 실행
assertEquals("123", num);
}
}
public interface GameNumGen {
String generate(GameLevel level);
}
- 행위 검증 = 실제로 모의 객체가 불렸는지 검증하는 것
- BDDMockito.then() = 메서드 호출 여부를 검증할 모의 객체를 전달받는다.
ex) 정확한 값 -> then(genMock).should().generate(GameLevel.EASY);
메서드가 불렸는지 여부 -> then(genMock).should().generate(any());
정확하게 한번만 호출됐는지 -> then(genMock).should(only()).generate(any());
- BDDMockito.then() = 메서드 호출 여부를 검증할 모의 객체를 전달받는다.
부록 D. AssertJ 소개
- JUnit은 테스트 실행을 위한 프레임워크를 제공하지만 한 가지 부족한 점이 있다. 단언에 대한 표현력이 부족하다는 점이다.
assertTrue(id.contains("a")); 의 경우 실패하면 실패 메시지는 true와 false밖에 보여주지 않는다.
이럴 때 AssertJ를 사용하면 assertThat(id).contains("a"); 와 같이 표현하여 실패 메시지는 id가 "a"를 포함하지 않아 실패했다고 알 수 있다. - AssertJ를 사용하면 테스트 코드의 표현력이 높아질 뿐만 아니라 개발 도구의 자동 완성 기능을 활용할 수 있다.
- 의존 설정
build.gradle에 아래 내용 추가
testImplementation 'org.assertj:assertj-core:3.25.1'
- 기본 검증 메서드 -> assertThat(실제값). + 아래 메서드
- isEqualTo(값) : 값과 같은지 검증
- isNotEqualTo(값) : 값과 같지 않은지 검증
- isNull() : null인지 검증
- isNotNull() : null이 아닌지 검증
- isIn(값 목록) : 값 목록에 포함되어 있는지 검증
- isNotIn(값 목록) : 값 목록에 포함되어 있지 않은지 검증
- isLessThan(값) : 값보다 작은지 검증
- isLessThanOrEqualTo(값) : 값보다 작거나 같은지 검증
- isGreaterThan(값) : 값보다 큰지 검증
- isGreaterThanOrEqualTo(값) : 값보다 크거나 같은지 검증
- isBetween(값1, 값2) : 값1과 값2 사이에 포함되는지 검증
- isTrue() : 값이 true인지 검증
- isFalse() : 값이 false인지 검증
- String에 대한 추가 검증 메서드
- contains(CharSequence... values) : 인자로 지정한 문자열들을 모두 포함하고 있는지 검증
- containsOnlyOnce(CharSequence sequence) : 해당 문자열을 딱 한 번만 포함하는지 검증
- containsOnlyDigits() : 숫자만 포함하는지 검증
- containsWhitespaces() : 공백 문자를 포함하고 있는지 검증
- containsOnlyWhitespaces() : 공백 문자만 포함하는지 검증
- containsPattern(CharSequence regex) 또는 containsPattern(Pattern pattern)
: 지정한 정규 표현식에 일치하는 문자를 포함하는지 검증 - doesNotContain(CharSequence... values) : 인자로 지정한 문자열들을 모두 포함하고 있지 않은지 검증
- doesNotContainAnyWhitespaces() : 공백 문자를 포함하고 있지 않은지 검증
- doesNotContainOnlyWhitespaces() : 공백 문자만 포함하고 있지 않은지를 검증
- doesNotContainPattern(CharSequence regex) 또는 doesNotContainPattern(Pattern pattern)
: 정규 표현식에 일치하는 문자를 포함하고 있지 않은지를 검증 - startsWith(CharSequence prefix) : 지정한 문자열로 시작하는지를 검증
- doesNotStartWith(CharSequence prefix) : 지정한 문자열로 시작하지 않는지를 검증
- endsWith(CharSequence suffix) : 지정한 문자열로 끝나는지를 검증
- doesNotEndWith(CharSequence suffix) : 지정한 문자열로 끝나지 않는지를 검증
- 숫자에 대한 추가 검증 메서드
- isZero() / isNotZero() : 0인지 또는 0이 아닌지를 검증
- isOne() : 1인지를 검증
- isPositive() / isNotPositive() : 양수인지 또는 양수가 아닌지를 검증
- isNegative() / isNotNegative() : 음수인지 또는 음수가 아닌지를 검증
- 날짜/시간에 대한 검증 메서드
- isBefore(비교할 값) : 비교할 값보다 이전인지 검증
- isBeforeOrEqualTo(비교할 값) : 비교할 값보다 이전이거나 같은지 검증
- isAfter(비교할 값) : 비교할 값보다 이후인지 검증
- isAfterOrEqualTo(비교할 값) : 비교할 값보다 이후이거나 같은지 검증
- isEqualToIgnoringNanos(비교할 값), isEqualToIgnoringSeconds(비교할 값), isEqualToIgnoringMinutes(비교할 값), isEqualToIgnoringHours(비교할 값) : LocalDateTime, OffsetDataTime, ZonedDateTime 타입에서 사용
- 콜렉션에 대한 검증 메서드
- List, Set
- hasSize(int expected) : 콜렉션의 크기가 지정한 값과 같은지 검증
- contains(E ... values) : 콜렉션이 지정한 값을 포함하는지 검증
- containsOnly(E ... values) : 콜렉션이 지정한 값만 포함하는지 검증
- containsAny(E ... values) : 콜렉션이 지정한 값 중 일부를 포함하는지 검증
- containsOnlyOne(E ... values) : 콜렉션이 지정한 값을 한 번만 포함하는지 검증
- Map
- containsKey(K key) : Map이 지정한 키를 포함하는지 검증
- containsKeys(K... keys) : Map이 지정한 키들을 포함하는지 검증
- containsOnlyKeys(K... keys) : Map이 지정한 키만 포함하는지 검증
- doesNotContainKeys(K... keys) : Map이 지정한 키들을 포함하지 않는지 검증
- containsValues(VALUE... values) : Map이 지정한 값들을 포함하는지 검증
- contains(Entry<K,V>... value) : Map이 지정한 Entry<K,V>를 포함하는지 검증
- List, Set
- 익셉션 관련 검증 메서드
- assertThatThrownBy() : 인자로 받은 람다에서 익셉션이 발생하는지 검증
- 그 외 assertThatExceptionOfType(), assertThatIOException(), assertThatNullPointerException(), assertThatIllegalArgumentException(), assertThatIllegalStateException(), assertThatCode() 등
- 모아서 검증
- SoftAssertions : 여러 검증을 한 번에 수행하고 싶을 때 사용, JUnit 5의 assertAll()과 유사
- as()와 describedAs()로 설명 달기
ex) assertThan(id).as("ID 검사").isEqualTo("abc");
assertThan(id).as("ID 검사: %s", "abc").isEqualTo("abc");
반응형
'Methodology > TDD' 카테고리의 다른 글
[TDD] 테스트 코드 작성 순서 (0) | 2024.05.26 |
---|---|
Test-Driven Development, TDD 란? (0) | 2024.05.26 |