본문 바로가기

우테코/Level1

[우테코-Lv1] JUnit 단위 테스트 공부

JUnit5이란?

: 자바 언어를 사용하는 소프트웨어 개발자를 위한 테스트 프레임 워크

: 주로 단위 테스트를 작성하고 실행하는 데에 쓰인다.

 

참고 사이트


요약된 표

JUnit 테스트 프레임 워크
@Test  해당 메서드가 테스트임을 나타내는 애너테이션
@DisplayName  해당 테스트의 이름을 나타냄
@Nested 중첩 클래스임을 나타냄
@Disabled 해당 테스트를 비활성화
@assertEquals(expected, actual) 두 값이 같은지 비교(equals())
@asssertNotEquals(expected, actual) 두 값이 같지 않은지 비교(! equals())
@assertSame(expected, actual) 두 객체가 같은 객체인지(같은 메모리 주소를 가리키는지 확인)
@assertThrows(expectedType, executable) 특정 예외가 발생하면 테스트 통과
@assertDoesNotThrow(executable) 특정 예외가 발생하지 않으면 통과
@assertAll(executables) 여러 검증코드를 한번에 실행

@Test

point 1: 해당 메서드가 테스트 메서드임을 나타냄
point 2. 테스트 메서드는 void 타입을 반환

: 테스트 메서드는 의도를 나타내는 이름으로 작성하는 것이 좋음

 

point1 : 해당 메서드가 테스트 메서드임을 나타냄

 

void Test_애노테이션을_붙여_테스트_메서드로_만든다() {
}
@Test
void Test_애노테이션을_붙여_테스트_메서드로_만든다() {
}
@Test 가 없음 @Test 애너테이션을 붙임 => Test Code임을 알려줌

 

point 2. 테스트 메서드는 void 타입을 반환

@Test
int return_타입이_void가_아니라면_테스트_메서드가_아니다() {
}
@Test
void Test_애노테이션을_붙여_테스트_메서드로_만든다() {
}
반환형이 int => 테스트 메서드 X 반환형이 void => 테스트 메서드 O

 


@DisplayName

: 해당 테스트의 이름을 나타냄

: 테스트 메서드에 한글을 작성해도 동작은 하지만 3가지 문제가 발생

1. 도구 및 라이브러리와의 호환성
: 일부 개발 도구, 라이브러리 및 프레임워크는 한글을 지원하지 않습니다.

2. 언어 혼합
: 언어 식별자가 혼합된 코드베이스에서는 일관성을 유지하는 것이 어려울 수 있습니다. 코드의 일부에서는 영어 명명을 사용하고 다른 부분에서는 한글을 사용하면 전체 코드 가독성이 떨어질 수 있습니다.

3. 경고 발생
: 일부 IDE는 한글을 사용할 때 경고를 발생시킵니다. 무의미한 경고가 많다면 중요한 경고를 놓칠 수 있습니다.
: @DisplayName이 없다면 해당 메서드명에 'Non-ASCII characters` 경고가 발생
=> 경고 제거를 위해서 @DisplayName 애너테이션을 작성하고 메서드명은 영어 사용 권장

=> 따라서 테스트의 이름을 한글로 작성하고 싶다면 @DisplayName 애너테이션을 사용하는 것이 좋음

 

 

point : DisplayName 안에 테스트 한글 이름을 작성하면, 불필요한 경고 발생을 막을 수 있음

void use_displayName_ 애노테이션을_붙여_경고를_제거한다 (){
}
@DisplayName("애노테이션을_붙여_경고를_제거한다")
void use_displayName() {
}
@DisplayName() 작성 x => Non-ASCII characters 발생  @DisplayName() 작성 O => 경고 X

@Nested

: 중첩 클래스임을 나타냄


@Disabled

: 해당 테스트를 비활성화(실행하지 않음)

: @Disabled 애너테이션은 @Test와 함께 사용함

=> @Test (테스트 임을 나타냄 ) + @Disabled(비활성화)

@Disabled => 실행하지 않음
@Test
@DisplayName("@Disabled 애노테이션을 붙여줘야 테스트 메서드로서의 역할을 수행하지 않는다")
void Disabled_애노테이션을_붙여줘야_테스트_메서드로서의_역할을_수행하지_않는다() {
    throw new RuntimeException("항상 실패한다.");
}

 

 - @Disabled 애너테이션은 클래스에도 적용이 가능하며 클래스에 @Disabled 애너테이션을 붙이면  해당 클래스의 모든 테스트가 비활성화됨

 


@assertEquals(expected, actual)

: 두 값이 같은지 비교하는 메서드

: 두 값이 같으면 테스트는 성공하며, 두 값이 다르면 테스트는 실패함

: assertEquals 메서드는 `org.junit.jupiter.api.Assertions` 클래스에 정의되어 있음
* 따라서 `Assertions` 클래스를 `static`으로 임포트하여 사용하는 것이 좋습니다.

 

@Test
@DisplayName("assertEquals 메서드로 두 값이 같은지 비교한다")
void assertEquals_메서드로_두_값이_같은지_비교한다() {
    final var a = 1;
    final var b = 2;
    final var actual = a + b;

    final var expected = 3;

    assertEquals(expected, actual);
}
만약 expected와 actual 타입이 equals 메서드를 정의하고 있지 않다면 
=> assertEquals 메서드를 사용할 수 없습니다.
 class LocalObject {
        private final int value;

        public LocalObject(int value) {
            this.value = value;
        }


    }

    final var a = new LocalObject(1);
    final var b = new LocalObject(1);

    assertEquals(a, b); => LocalObject에 equals가 정의되어 있지 않아 오류 발생
}

 

 

* assertEquals(expected, actual, message) 는 테스트가 실패했을 때 출력되는 메시지를 지정할 수 있습니다.

* 'message'는 테스트가 실패했을 때 출력되는 메시지입니다.

 

@Test
@DisplayName("assertEquals 메서드로 두 값이 같은지 비교하며, 테스트가 실패했을 때 출력되는 메시지를 지정한다")
void assertEquals_메서드로_두_값이_같은지_비교하며_테스트가_실패했을_때_출력되는_메시지를_지정한다() {
    final var a = 1;
    final var b = 2;
    final var actual = a + b;

    final var expected = 4;

    assertEquals(expected, actual, "두 값이 일치하지 않습니다.");
    
}

=> 실행결과
값이 일치하지 않습니다.
Expected :4
Actual   :3

@asssertNotEquals(expected, actual)

: 두 값이 다르다면 테스트를 성공시킨다.

@Test
@DisplayName("assertNotEquals 메서드로 두 값이 다른지 비교한다")
void assertNotEquals_메서드로_두_값이_다른지_비교한다() {
    final var a = 1;
    final var b = 2;
    final var actual = a + b;

    final var unexpected = 0;

    assertNotEquals(unexpected, actual);
}

 


@assertSame(expected, actual)

: 두 객체가 같은 객체인지 비교한다

: expected는 예상되는 객체이며, actual 은 실제 객체이다.

: 같은 객체(같은 메모리 주소를 가리키고 있는 것)라면 테스트에 통과한다

@Test
@DisplayName("assertSame 메서드로 두 객체가 같은지 비교한다")
void assertSame_메서드로_두_객체가_같은지_비교한다() {
    final var object = new Object();

    final var actual = object;
    final var expected = object;

    assertSame(expected, actual);
}

@assertThrows(expectedType, executable)

: 특정 예외가 발생하면 테스트 통과, 아니면 실패

: expectedType은 예외 타입

: executable은 예외가 발생할 코드

 

@Test
@DisplayName("assertThrows 메서드로 특정 예외가 발생하는지 비교한다")
void assertThrows_메서드로_특정_예외가_발생하는지_비교한다() {
    assertThrows(Exception.class, this::causeException);
}

 


@assertDoesNotThrow(executable)

: 특정 예외가 발생하지 않는지 확인한다.

: assertDoesNotThrow 를 사용하지 않아도 테스트는 통과하지만, 테스트의 의도를 명확하게 표현하기 위해 사용하는 것이 좋습니다.

@Test
@DisplayName("assertDoesNotThrow 메서드로 특정 예외가 발생하지 않는 것을 명시한다")
void assertDoesNotThrow_메서드로_특정_예외가_발생하지_않는_것을_명시한다() {
            assertDoesNotThrow(() -> {
                final var number = Integer.valueOf(0x80000000);
            });
}

 


@assertAll(executables)

: 여러 검증 코드를 한번에 실행함

: executables는 검증코드입니다.

: assertAll은 검증 코드 중 하나라도 실패한다면 테스트가 실패함

: assertAll을 사용하는 이유는 여러 검증코드가 존재할 때, 문제가 발생한다면 어떤  검증 코드에서 문제가 발생했는지 어렵기 때문에 사용한다.

=> 어떤 부분에서 오류가 발생했는지 빠르게 찾을 수 있음

 

@Test
@DisplayName("assertAll 메서드로 여러 검증 코드를 한 번에 실행한다")
void assertAll_메서드로_여러_검증_코드를_한_번에_실행한다() {
    assertAll(
            () -> assertEquals(1 + 2, 3),
            () -> assertEquals(2 + 3, 5),
            () -> assertEquals(7 * 3, 21),
            () -> assertEquals(7 * 3 ^ 5, 16),
            () -> assertEquals(7 * 3 / 5 + 33 / 21, 5),
            () -> assertEquals(33 * 3 / 5 + 7 / 2, 23),
            () -> assertEquals(33 * 3 / 5 + 7 / 2 + 1, 22)
    );

}