JUnit은 Java 에서 독립된 단위테스트를 지원해주는 프레임워크이기 때문에
단위 테스트에 대해 먼저 알아보겠습니다 💡
테스트 대상 단위가 엄격하게 정해져있지는 않지만, 일반적으로 클래스 또는 메소드 수준으로 정해집니다. 단위의 크기가 작을수록 단위의 복잡성이 낮아지고, 실행하려는 기능을 표현하기 더 쉬워지는게 당연하겠죠?
단위테스트란 특정 소스코드의 모듈이 의도한 대로 잘 작동하는지 검증하는 테스트입니다.
즉, 함수 및 메소드에 대한 테스트를 하는 작업으로써, Spring에서 단위테스트는 Spring Container에 올라와 있는 Bean 을 테스트 하는 것입니다.
단위 테스트는 해당 부분만 독립적으로 테스트하기 때문에 어떤 코드를 리팩토링하여도 빠르게 문제 여부를 파악할 수 있습니다.
또한, 개발자는 작성한 테스트 코드를 수시로 빠르게 돌리면서 문제점을 파악할 수 있습니다.
단위 테스트를 진행하는 동안에는 실제 어플리케이션을 동작시킬 때와 마찬가지로 Bean을 테스트 할 수 있습니다 🧑🏼💻
해당 구조에 대해 간단히 알아보겠습니다 💡
기존 버전인 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 Vintage : JUnit3, JUnit4 를 실행할 수 있는 TestEngine 입니다. 하위 호환성을 위해서 존재합니다.
TestEngine의 실제 구현체는 별도 모듈로 존재하는데, 이 중 하나가 jupiter-engine 입니다.
Test 역시 자바의 객체로 구성됩니다.
따라서 클래스로 구현을 하기 때문에 Class + Method 형태로 구성됩니다.
Class : 최상위 클래스, static member class, @Nested class 중 적어도 하나의 메소드를 포함하는 클래스
Test Method : @Test, @RepeatedTest, @TestFactory 등의 어노테이션이 선언되어 있는 메서드로 실제 테스트하고자 하는 내용을 넣습니다.
@Test
: 해당 메소드가 테스트 메소드임을 나타냅니다.
@ParametherizedTest
: 해당 메소드가 매개변수가 있는 테스트임을 나타냅니다.
@RepeatedTest
: 반복되는 테스트 메소드를 나타냅니다. -> @RepeatedTest(3)
🔄 전체적인 Life Cycle
BeforeAll -> BeforeEach -> Test -> AfterEach -> AfterAll
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
@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
@Test
@DisplayName("assertNotNull & assertNull Test")
public void test03() {
Object obj01 = null;
Object obj02 = new String("오늘은 2023/01/07");
assertNull(obj01);
assertNotNull(obj02);
}
assertTrue & assertFalse
@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
@Test
public void test() {
try {
methodThatShouldThrowException();
fail("Exception not thrown");
} catch (UnsupportedOperationException e) {
assertEquals("Operation Not Supported", e.getMessage());
}
}
assertThat
@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
@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
@Test
@DisplayName("assertTimeout Test")
void assertTimeoutTest(){
assertTimeout(Duration.ofMillis(1000), () -> {
new String("good morning");
Thread.sleep(300);
});
}
}
AssertJ 는 JUnit Assertions을 사용함에 있어 더 우아한(?) 테스트 작성을 도와주는 라이브러리 입니다 👍
spring-boot-starter-test 에 포함되어 있습니다 💪
Hamcrest와 비슷한 기능의 메소드를 제공합니다.
또한 핵심 메소드는 assertThat 1️⃣ 개 이며 메소드 체이닝 방식을 사용합니다.
Hamcrest 프레임워크사용하기_1 을 참조해주세요.
두 객체의 동일성이나 객체의 필드를 조사하기 위해 사용
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 값 비교를 위해 사용
Example
assertThat("".isEmpty()).isTrue(); // true
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를 직관적으로 테스트 할 수 있습니다 💪
JUnit5는 자바를 이용하는 개발자라면 당연히 숙지해야할 테스팅 기법입니다 📚
특히 요즘 개발 문화에 TDD 는 중요한 요소로 자리 잡았기에 더욱 필수적으로 익혀야 한다고 생각합니다 🙆🏻