Java
ddd
What ?
Junitsddd
dddfs
- 자바 개발자가 가장 많이 사용하는 테스팅 기반 프레임워크 입니다.
- 현재 가장 많이 사용되고 있는 버전은 JUnit 5 입니다.
- 기본 Junit 4에 비해 JUnit 5는 3가지 모듈로 구성됩니다.
- JUnit5는 테스트 작성자를 위한 API 모듈과 테스트 실행을 위한 API가 분리되어 있습니다.
- 자바 8 이상을 필요로 합니다.
- JUnit은 테스트 코드를 작성하고, 작성한 테스트 코드를 실행할 때 사용하는 자바의 테스트 프레임워크이다. 사실 JUnit은 리포지터리뿐만 아니라 소프트웨어 개발 시 테스트 작업을 수행할 때 많이 사용한다.
🧪 JUnit 구조

해당 구조에 대해 간단히 알아보겠습니다 💡
기존 버전인 JUnit4에서는 vintage-engine 모듈을 사용해서 테스팅을 진행했습니다.
그러나 이와 달리, JUnit5는 크게 3가지 모듈로 구성됩니다.
JUnit5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
JUnit Jupiter API : JUnit5에 새로 추가된 테스트 코드용 API
- JUnit Platform : 테스트를 발견하고 테스트 계획을 생성하는 TestEngine 인터페이스를 정의 합니다.
즉, 기초적인 역할을 수행하며 TestEngine API 를 제공합니다. - JUnit Jupiter : JUnit5에서 테스트 및 Extension을 작성하기 위한 새로운 프로그래밍 모델과 확장 모델의 조합이며, TestEngine API 구현체 입니다.
JUpiter 는 테스트 코드 작성에 필요한 junit-jupiter-api 모듈과 테스트 실행을 위한 junit-jupiter-engine 모듈로 분리되어 있다.- JUnit Jupiter = junit-jupiter-api + junit-jupiter-engine
- JUnit Vintage : JUnit3, JUnit4 를 실행할 수 있는 TestEngine 입니다. 하위 호환성을 위해서 존재합니다.
TestEngine의 실제 구현체는 별도 모듈로 존재하는데, 이 중 하나가 jupiter-engine 입니다.
🧪 테스트의 Class & Method 어노테이션
Test 역시 자바의 객체로 구성됩니다.
따라서 클래스로 구현을 하기 때문에 Class + Method 형태로 구성됩니다.
- Class : 최상위 클래스, static member class, @Nested class 중 적어도 하나의 메소드를 포함하는 클래스
- Test Class 는 absctract 이면 안되며, 기본 생성자를 포함해야 합니다 💪
- Test Method : @Test, @RepeatedTest, @TestFactory 등의 어노테이션이 선언되어 있는 메서드로 실제 테스트하고자 하는 내용을 넣습니다.
- @Test
: 해당 메소드가 테스트 메소드임을 나타냅니다. - @ParametherizedTest
: 해당 메소드가 매개변수가 있는 테스트임을 나타냅니다. - @RepeatedTest
: 반복되는 테스트 메소드를 나타냅니다. -> @RepeatedTest(3)
- @Test
- LifeCycle Method : @BeforeAll, @BeforeEach, @AfterAll, @AfterEach 가 선언되어 있는 메서드로 Test 의 Life Cycle에 따라 실행되는 메소드 입니다.
- @BeforeAll
: 테스트 Class 기준으로 테스트 메서드들이 실행되기 전 실행
즉, 테스트 시작 시점에 딱 한번 실행(static 메소드만 가능) - @BeforeEach
: 각 테스트 메서드가 실행되기 전 실행
일반적으로 Mock 데이터를 미리 세팅하기 위해 사용 - @AfterAll
: 테스트 Class 기준을 테스트 메서드들이 실행된 후 실행
즉, 테스트 종료 시점에 딱 한번 실행(static 메소드만 가능) - @AfterEach
: 각 테스트 메서드가 실행되기 전 실행
- @BeforeAll
🔄 전체적인 Life Cycle
BeforeAll -> BeforeEach -> Test -> AfterEach -> AfterAll
🧪 기타 어노테이션
- @Tag : 테스트 클래스, 메소드에 테스트 구분을 태킹하기 위해 사용
- @Disabled : 해당 클래스나 테스트를 사용하지 않음
- @Timeout : 테스트 실행 시간 선언, 초과되면 실패하도록 설정
- @ExtendWith : 확장을 선언적으로 등록할 때 사용
- @TempDir : 필드 주입 혹은 매개변수 주입을 통해 임시 디렉토리 제공
- @DispayName : 테스트 메소드 이름 설정(기본값 -> 메소드 이름)
🧪 Assertions
org.junit.jupiter.api.Assertions 패키지를 통해 사용 가능
- assertEquals
- 두 값을 비교하여 일치 여부 판단
@Test
@DisplayName("assertEquals Test")
public void test () {
String expected = "2023/01/07";
String actual = "2023/01/07";
assertEqauls(expected, actual); // Test Succeed!
}
- assertArrayEqauls
- 두 배열을 비교하여 일치 여부 판단
- 동등성을 비교하며, 두 배열이 null 이여도 동일한 것으로 판단
@Test
@DisplayName("assertArrayEquals Test")
public void test02() {
char[] expected = {'J','u','n','i','t'};
char[] actual = "Junit".toCharArray();
System.out.println(expected == actual); // 동일성 측면에서는 false
assertArrayEquals(expected, actual); // 동등성 측면에서는 true
}
- assertNotNull & assertNull
- 객체의 null 여부 확인
@Test
@DisplayName("assertNotNull & assertNull Test")
public void test03() {
Object obj01 = null;
Object obj02 = new String("오늘은 2023/01/07");
assertNull(obj01);
assertNotNull(obj02);
}
- assertTrue & assertFalse
- 특정 조건이 true인지 false 인지 판단
@Test
@DisplayName("assertTrue & assertFalse Test")
public void test04() {
assertTrue(5 > 4,"5 is greater than 4");
assertFalse(5 > 6, " 5 is not greater than 6"); // Message 설정도 가능
}
- fail
- AssertionFailedError를 발생시키는 테스트에 실패
- 실제 예외가 발생했는지 확인하거나, 개발 중에 테스트를 실패하게 만들고 싶을 때 사용
@Test
public void test() {
try {
methodThatShouldThrowException();
fail("Exception not thrown");
} catch (UnsupportedOperationException e) {
assertEquals("Operation Not Supported", e.getMessage());
}
}
- assertThat
- 첫번째 파라미터에 비교대상 값, 두번째 파라미터에 비교 로직이 담긴 Matcher 를 받음
@Test
@DisplayName("assertThat Test")
public void test05(){
// hamcrest 라이브러리 -> JUnit 에 사용되는 Matcher 라이브러리
// spring-boot-starter-test 기본적으로 포함
assertThat("Sample String", is(not(startsWith("Test"))));
// when
// then
}
- assertThrows
- 사용자가 선택한 예외가 발생하는지 여부를 판단
@Test
@DisplayName("assertThrows Test")
public void test06(){
// given
IllegalArgumentException exception =
assertThrows(IllegalArgumentException.class, () -> {
DoSomething.func();
});
// when
// then
}
- assertAll
- 해당하는 모든 assertion 체크
- 하나라도 실패시 그 밑의 코드는 더 이상 진행하지 않음
@Test
@DisplayName("assertAll Test")
void assertAllTest(){
// given
String s1 = "Hello";
String s2 = "Hello";
assertAll(
() -> assertNotNull(s1),
() -> assertEquals(s2,s1),
() -> assertTrue(s1.length() > 2)
);
// when
// then
}
- assertTimwout
- 지정한 시간 내에 assertion 실행이 완료되는지 확인
@Test
@DisplayName("assertTimeout Test")
void assertTimeoutTest(){
assertTimeout(Duration.ofMillis(1000), () -> {
new String("good morning");
Thread.sleep(300);
});
}
}
🧪 AssertJ 라이브러리
AssertJ 는 JUnit Assertions을 사용함에 있어 더 우아한(?) 테스트 작성을 도와주는 라이브러리 입니다 👍
spring-boot-starter-test 에 포함되어 있습니다 💪
Hamcrest와 비슷한 기능의 메소드를 제공합니다.
또한 핵심 메소드는 assertThat 1️⃣ 개 이며 메소드 체이닝 방식을 사용합니다.
Hamcrest 프레임워크사용하기_1 을 참조해주세요.
💡 Object Assertions
두 객체의 동일성이나 객체의 필드를 조사하기 위해 사용
Example
public class Dog {
private String name;
private Double weight;
}
@Test
public class DogTest() {
Dog dog01 = new Dog("진돗개", 3.45);
Dog dog02 = new Dog("시추" , 1.23);
assertThat(dog01).isEqualTo(dog02);
}
isEqualsTo() 는 객체의 동일성을 비교하므로 해당 테스트는 fail입니다 🥲
동등성을 비교하기 위해서는 isEqualToComparingFieldByFieldRecursively() 을 사용합니다.
💡 Boolean Assertions
두 객체의 Boolean 값 비교를 위해 사용
- isTrue()
- isFalse()
Example
assertThat("".isEmpty()).isTrue(); // true
💡 Iterable/Array Assertions
Iterable/Array 에 특정 요소가 존재하는지 확인 등의 다양한 목적으로 사용
List<String> list = Arrays.asList("1","2","3");
assertThat(list).contains("1"); // 포함되었는지 확인
assertThat(list).isNotEmpty(); // 비어있는지 확인
assertThat(list).startsWith("1"); // 해당 요소로 시작하는지 확인
assertThat(list).doesNotContainNull() // null 포함 하지 않는지 확인
이외에도 AssertJ 를 이용해서 다양한 Case를 직관적으로 테스트 할 수 있습니다 💪