테스트 코드는 사실 공부할 생각이 없었습니다..
하지만 요번 3주차 미션을 진행하면서 기능이 변경될 때마다 모든 기능을 다시 테스트하는 일을 쉽지가 않았습니다.
그래서 테스트 코드에 도전해보기로 했습니다! 💪
Java에서 독립된 단위 테스트(Unit Test)를 지원해주는 프레임워크입니다.
@
)으로 간결하게 사용 가능소스 코드의 특정 모듈이 의도된 대로 작동하는 지 확인하는 검증 절차입니다.
Annotation | Description |
---|---|
@Test | 테스트 함수임을 보여줍니다. |
@ParameterizedTest | 매개변수가 있는 함수의 경우 사용합니다. |
@RepeatedTest | 반복 테스트를 위한 테스트 템플릿입니다. |
@TestFactory | 동적 테스트를 위한 테스트 팩토리입니다. |
@TestTemplate | 등록된 공급자가 반환한 호출 컨텍스트 수에 따라 여러번 호출되도록 설계된 테스트케이스 입니다. |
@TestMethodOrder | 테스트 함수의 실행 순서를 정하는데 사용됩니다. |
@DisplayName | 테스트 클래스 또는 함수에 대해 사용자가 지정한 이름을 표시해줍니다. |
@DisplayNameGeneration | 테스트 클래스에 대해 사용자가 지정한 이름을 생성합니다. |
@BeforeEach | 테스트 함수보다 먼저 실행됩니다. |
@AfterEach | 테스트 함수가 끝난 후에 실행됩니다. |
@BeforeAll | 클래스 내부의 모든 테스트 함수보다 먼저 실행되며 static이어야 합니다. |
@AfterAll | 클래스 내부의 모든 테스트 함수 이후에 실행되며 static이어야합니다. |
@Nested | 중첩된 구조로 테스트를 가능하게 합니다. |
@Tag | 클래스 또는 함수에서 테스트를 필터링 하기위해 태그를 선언합니다. |
@Disabled | 테스트 클래스 또는 테스트 메서드를 비활성화 하는데 사용됩니다. |
@Timeout | 실행시간이 초과되는 경우 테스트 함수가 실패하는데 사용됩니다. |
@ExtendWith | 선언적으로 단위 테스트간에 공통적으로 사용할 기능을 구현하여 확장합니다. |
@RegisterExtension | 절차적 코드를 이용하여 공통 기능을 구현하고 확장합니다. |
@TempDir | 라이프 사이클 방법 또는 테스트 방법에서 필드 주입 또는 매개 변수 주입을 통해 임시 디렉토리를 제공하는 데 사용됩니다. |
@Tag("example")
public @interface MyInteface {
}
// tast 어노테이션 사용
@Example
@Test
void myInterfaceTest() {
// ...
}
@Test
void 맨_앞에_구분자_입력() {
assertThrows(InvalidCarNameException.class ,
() -> InputValidator.validNameInput(",이름1"));
}
@BeforeAll
static void 제일_먼저_실행() {
System.out.print("하윙\n");
}
테스트가 수행되는 메소드를 의미
Junit은 각각의 테스트가 서로 영향을 주지 않고 독립적으로 실행됨을 원칙으로 @Test
마다 객체를 생성합니다.
테스트 클래스는 최소 하나 이상의 테스트 메서드를 포함하는 최상위 클래스여야 하며 abstract
나 interface
이면 안되고 단일 생성자여야만 합니다.
class Test {
@BeforeAll
static void 초기_테스트_클래스_동작시_처음_수행() {
}
@BeforeEach
void 테스트_함수_동작_이전에_수행() {
}
@Test
void 테스트_함수() {
}
@Test
void 실패용_테스트_함수() {
fail("실패 테스트");
}
@Test
@Disabled("비활성화 예제")
void 활성화_되지_않은_테스트_함수() {
// 테스트가 수행되지 않습니다.
}
@Test
void 테스트_함수_2() {
assumeTrue("가나다".equals("라마"));
fail("테스트가 실패하였습니다.");
}
@AfterEach
void 테스트_함수_끝난_후_동작() {
System.out.print("테스트_함수_끝난_후_동작");
}
@AfterAll
static void 테스트_클래스가_끝난_후_동작() {
System.out.print("테스트_클래스가_끝난_후_동작");
}
}
테스트_함수_끝난_후_동작()
은 총 3번 출력, 테스트_클래스가_끝난_후_동작()
은 1번 출력됩니다.
실패용_테스트_함수()
테스트_함수_2()
테스트 함수 앞에 이 태그를 붙이고 이름을 설정해주면 다음과 같이 이름을 표시할 수 있습니다.
Annotation | Description |
---|---|
@assertAll(executeables ...) | 구문 오류시 예외를 발생시키지 않으면서 한번에 모든 구문을 확인할 수 있습니다. |
@assertEquals(expected, actual) | 실제 값과 예상 값이 같은지 확인합니다. 이외 assertArrayEquals() , assertNotEquals() 도 존재합니다. |
@assertNotNull(actual) | 값이 NULL인지 확인합니다. |
@assertTrue(Boolean) | 다음 조건이 참인지 확인합니다. |
@assertThrows(expectedType, executable) | 예외를 발생 시키는 지 확인합니다. |
@assertTimeout(timeout, executable) | 특정 시간 안에 실행하는 지 확인합니다. |
@assertTimeoutPreemptively(timeout, executable) | 특정 시간 안에 실행하는 지 확인 후, 시간을 초과하면 실행이 중단되도록 설정합니다. 단, executable과 다른 스레드에서 실행하기 때문에 원치 않는 결과가 발생할 수 있습니다. (ex. 트랜잭션이 적용이 안되서 롤백이 안 되는 경우 ) |
class GameControllerTest {
@DisplayName("AssertNotNull 함수")
@Test
public void 입력이_널인지_확인() {
assertNotNull("하윙");
}
@DisplayName("AssertEquals 함수")
@Test
public void 값이_같은_지_확인() {
String str = "입력";
assertEquals("입력", str);
}
@DisplayName("AssertAll 함수")
@Test
public void 한번에_모든_구문을_확인() {
List<String> list = Arrays.asList("일", "이", "삼", "사");
for (String str : list) {
assertAll("comment",
() -> assertNotNull(str),
() -> assertNotEquals("영", str));
}
}
@DisplayName("AssertTrue")
@Test
public void 값이_참인지_확인() {
assertTrue(5 > 4, () -> "5는 4보다 크다");
}
@DisplayName("AssertTimeOut")
@Test
void 작업_시간이_얼마나_초과했는지_체크() {
assertTimeout(Duration.ofMillis(100), () -> {
Thread.sleep(99);
});
}
@DisplayName("AssertTimeoutPreemptively")
@Test
void 작업_시간_초과_시_에러_메세지() {
assertTimeoutPreemptively(Duration.ofMillis(101), () -> {
Thread.sleep(1);
});
}
}
테스트코드에서 그동안 정말 많이 찾아보고 봐왔었는데,
여태까지는 왜 이렇게 이해가 안 갔던 것인지..
이번에 차분하게 다시 정리해보았는데요, 이제 조금씩 이해가 가기 시작하네여
이어지는 글로 제가 직접 사용한 테스트 코드를 가져와보겠습니다.😊