πŸ“– λ‚΄κ°€ μ •λ¦¬ν•œ JUnit5


μš°ν…Œμ½” level1 1μ£Όμ°¨ - JUnit을 ν™œμš©ν•œ λ‹¨μœ„ ν…ŒμŠ€νŠΈ 이둠 및 μ‹€μŠ΅ λ‹¨μœ„ν…ŒμŠ€νŠΈμ— λŒ€ν•΄μ„œ λ°°μ› λ‹€. ν—ˆλ‚˜ λ‚˜λŠ” λ‹¨μœ„ ν…ŒμŠ€νŠΈλŠ” 처음이라 πŸ‘€ 처음 배운 JUnit5λ₯Ό 읡히기 μœ„ν•΄ 정리해본닀.

계속 μΆ”κ°€ μ˜ˆμ •βœοΈ

JUnitμ΄λž€

  • μžλ°” ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄λ₯Ό μœ„ν•œ λ‹¨μœ„ ν…ŒμŠ€νŠΈ ν”„λ ˆμž„ μ›Œν¬
  • xUnit으둜 μ•Œλ €μ§„ λ‹¨μœ„ ν…ŒμŠ€νŠΈ ν”„λ ˆμž„ μ›Œν¬ μ œν’ˆκ΅°μ΄λ‹€.
  • β€œλ¨Όμ € ν…ŒμŠ€νŠΈ ν•œ λ‹€μŒ 코딩”
  • β€œμ‘°κΈˆ ν…ŒμŠ€νŠΈ, μ•½κ°„ μ½”λ”©, μ•½κ°„ ν…ŒμŠ€νŠΈ, μ•½κ°„ 코딩”

νŠΉμ§•

  • ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•˜κ³  μ‹€ν–‰ν•˜λŠ” 데 μ‚¬μš©λ˜λŠ” μ˜€ν”ˆ μ†ŒμŠ€ ν”„λ ˆμž„ μ›Œν¬
  • annotation을 μ œκ³΅ν•œλ‹€.
  • ν…ŒμŠ€νŠΈ κΈ°λŒ€ κ²°κ³Όλ₯Ό μœ„ν•œ assertion을 μ œκ³΅ν•œλ‹€.
  • test runnerλ₯Ό μ œκ³΅ν•œλ‹€.
  • 퀄리티λ₯Ό 올리며 λΉ λ₯΄κ²Œ μ½”λ“œλ₯Ό μž‘μ„±ν•  수 있게 ν•΄μ€€λ‹€.
  • μžλ™μœΌλ‘œ ν…ŒμŠ€νŠΈκ°€ 싀행될 수 있으며 자체의 κ²°κ³Όλ₯Ό ν™•μΈν•˜κ³  λ°”λ‘œ κ²°κ³Όλ₯Ό μ œκ³΅ν•œλ‹€.
  • ν…ŒμŠ€νŠΈ cases 및 기타 ν…ŒμŠ€νŠΈ suitesλ₯Ό ν¬ν•¨ν•˜λŠ” test suites둜 κ΅¬μ„±λœλ‹€.
  • ν…ŒμŠ€νŠΈ μ„±κ³΅μ‹œ 초둝, μ‹€νŒ¨μ‹œ λΉ¨κ°•

λ‹¨μœ„ ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€λŠ” μ™œ 써?

  • λ©”μ†Œλ“œκ°€ μ—μƒλŒ€λ‘œ λ™μž‘ν•˜λŠ”μ§€ ν™•μΈν•œλ‹€.
  • μ›ν•˜λŠ” κ²°κ³Όλ₯Ό λΉ λ₯΄κ²Œ ν™•μΈν•˜κΈ° μœ„ν•΄μ„œλŠ” ν…ŒμŠ€νŠΈ ν”„λ ˆμž„ μ›Œν¬κ°€ ν•„μš”ν•˜λ‹€.
  • 이 역할을 JUnit이 ν•œλ‹€.
  • μ•Œκ³  μžˆλŠ” μž…λ ₯κ³Ό κΈ°λŒ€ν•˜λŠ” 좜λ ₯으둜 μž‘μ„±λœλ‹€.
  • 각 μš”κ΅¬ 사항에 λŒ€ν•΄ μ΅œμ†Œ 두 개의 λ‹¨μœ„ ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€κ°€ ν•„μš”ν•˜λ‹€. (μ–‘μ„±, μŒμ„±)
  • μš”κ΅¬μ‚¬ν•­μ— ν•˜μœ„ μš”κ΅¬μ‚¬ν•­μ΄ 있으면 여기에도 두 개의 λ‹¨μœ„ ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€κ°€ ν•„μš”ν•˜λ‹€.

JUnit5 πŸ‘€

λͺ¨λ“ˆ ꡬ성

JUnit Platform

ν…ŒμŠ€νŠΈ ν”„λ ˆμž„μ›Œν¬λ₯Ό μ‹€ν–‰ν•˜λŠ”λ° 기반이 λ˜λŠ” λͺ¨λ“ˆ ν”Œλž«νΌμ—μ„œ λ™μž‘ν•˜λŠ” ν…ŒμŠ€νŠΈ ν”„λ ˆμž„ μ›Œν¬ κ°œλ°œμ„ μœ„ν•œ TestEngine APIλ₯Ό μ •μ˜ν•œλ‹€. command lineμ—μ„œ console Launcher와 JUnit4 기반 ν™˜κ²½μ˜ ν”Œλž«νΌμ—μ„œ μ‹€ν–‰ν•˜κΈ° μœ„ν•œ JUnit 4 based Runner λ₯Ό μ œκ³΅ν•œλ‹€. 각쒅 IDE 및 λΉŒλ“œ 도ꡬ에 μ‘΄μž¬ν•œλ‹€.

JUnit Jupiter

ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•œ μƒˆλ‘œμš΄ ν”„λ‘œκ·Έλž˜λ° λͺ¨λΈ κ³Ό ν™•μž₯ λͺ¨λΈμ˜ 쑰합이닀. μ£Όν”Όν„°μ˜ ν•˜μœ„ ν”„λ‘œμ νŠΈλŠ” ν”Œλž«νΌμ—μ„œ μ£Όν”Όν„° λ™μž‘μ„ μœ„ν•œ TestEngine을 μ œκ³΅ν•œλ‹€.

JUnit Vintage

ν•˜μœ„ 버전을 μ‹€ν–‰ν•  수 μžˆλŠ” TestEngine을 μ œκ³΅ν•œλ‹€.

Installation

mavenμ΄λ‚˜ gradle ν”„λ‘œμ νŠΈμ—μ„œ Jupiter Engine Dependency and Platform Runner Dependency 즉 μ΅œμ†Œ 2개의 dependencyκ°€ ν•„μš”ν•˜λ‹€.

maven

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>${maven.compiler.source}</maven.compiler.target>
    <junit.jupiter.version>5.5.2</junit.jupiter.version>
    <junit.platform.version>1.5.2</junit.platform.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>${junit.jupiter.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-runner</artifactId>
        <version>${junit.platform.version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>

gradle

testRuntime("org.junit.jupiter:junit-jupiter-engine:5.5.2")
testRuntime("org.junit.platform:junit-platform-runner:1.5.2")

Java Versions

Java8 이상이어야 ν•œλ‹€.

Tests μž‘μ„±ν•˜κΈ°

이 뢀뢄은 κ°•μ˜ μ‹œκ°„μ— λ‹€λ€˜λ˜ μ½”λ“œλ‘œ!

...

public class SetTest {
    private Set<Integer> numbers;

    @Test
    void checkSizeOfSet(){
        assertThat(numbers.size()).isEqualTo(4);
    }

}

Test Class

μ΅œμ†Œ ν•˜λ‚˜μ˜ ν…ŒμŠ€νŠΈ λ©”μ†Œλ“œλ₯Ό ν¬ν•¨ν•˜λŠ” μ΅œμƒμœ„ 클래슀 single constructor μ—¬μ•Ό ν•œλ‹€.

Test Method

@Test, @ParameterizedTest λ“±μ˜ μ–΄λ…Έν…Œμ΄μ…˜μ„ 톡해 ν…ŒμŠ€νŠΈ λ©”μ†Œλ“œμž„μ„ λ‚˜νƒ€λ‚Έλ‹€.

  • assertJλ₯Ό 톡해 ν…ŒμŠ€νŠΈ μ½”λ“œμ˜ 가독성을 높이며 μ—λŸ¬ 메세지λ₯Ό μ‰½κ²Œ 확인할 수 μžˆλ‹€.

κ³΅λΆ€ν•œ Annotation λͺ‡ 개

@Test

λ©”μ„œλ“œκ°€ ν…ŒμŠ€νŠΈ λ©”μ„œλ“œμž„μ„ λ‚˜νƒ€λ‚Έλ‹€.

@ParameterizedTest

λ©”μ„œλ“œκ°€ λ§€κ°œλ³€μˆ˜κ°€ μžˆλŠ” ν…ŒμŠ€νŠΈμž„μ„ λ‚˜νƒ€λ‚Έλ‹€.

νŒŒλΌλ―Έν„°λ₯Ό μ „λ‹¬ν•˜λŠ” 방법은 μ•„λž˜μ˜ μ–΄λ…Έν…Œμ΄μ…˜κ³Ό λ”λΆˆμ–΄ Source둜 λ„˜κ²¨μ€€λ‹€.

@ValueSource

단일 λ¦¬ν„°λŸ΄ κ°’ 배열을 ν…ŒμŠ€νŠΈ λ©”μ„œλ“œμ— 전달할 수 있으며, 맀번 ν…ŒμŠ€νŠΈ λ©”μ„œλ“œμ— ν•˜λ‚˜μ˜ 인수 만 전달할 수 μžˆλ‹€. short, byte, int, long, float, double, char, boolean, java.lang.String, java.lang.Class λ¦¬ν„°λŸ΄ 값을 μ§€μ›ν•œλ‹€.

@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
void contains1(int i) {
    assertThat(numbers.contains(i)).isTrue();
}

@CsvSource

μ—¬λŸ¬ νŒŒλΌλ―Έν„°λ₯Ό μ „λ‹¬ν•˜κ³ μž ν•  λ•Œ μ‚¬μš©ν•œλ‹€. 주둜 μž…λ ₯ κ°’κ³Ό κΈ°λŒ€ 값을 ν•¨κ»˜ μ „λ‹¬ν•˜λ©° ν…ŒμŠ€νŠΈν•  λ•Œ μ‚¬μš©ν•œλ‹€. ,둜 값을 κ΅¬λΆ„ν•˜μ§€λ§Œ delimiter 속성을 μ‚¬μš©ν•΄ μ‚¬μš©μž μ •μ˜λ₯Ό ν•  수 μžˆλ‹€.

@ParameterizedTest
@CsvSource(value = {"1:true", "5:false"}, delimiter = ':')
void contains3(int i, boolean expected) {
 assertThat(expected).isEqualTo(numbers.contains(i));
}

@NullSource

단일 null 값을 전달할 수 μžˆλ‹€.

ν•˜μ§€λ§Œ κΈ°λ³Έ 데이터 νƒ€μž…μ€ null 값을 ν—ˆμš©ν•  수 μ—†μ–΄ @NullSourceλ₯Ό μ‚¬μš©ν•  수 μ—†λ‹€.

@EmptySource

λ©”μ„œλ“œμ— ν•˜λ‚˜μ˜ 빈 값을 μΈμˆ˜μ— μ „λ‹¬ν•œλ‹€.

@ParameterizedTest
@EmptySource
void isBlank_ShouldReturnTrueForEmptyStrings(String input) {
    assertTrue(Strings.isBlank(input));
}

@NullAndEmptySource

nullκ³Ό empty κ°’ λͺ¨λ‘λ₯Ό μ „λ‹¬ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•œλ‹€.

@ParameterizedTest
@NullAndEmptySource
void isBlank_ShouldReturnTrueForNullAndEmptyStrings(String input) {
    assertTrue(Strings.isBlank(input));
}

βž• nullκ³Ό empty κ°’, λ³€ν˜•λœ 빈 λ¬Έμžμ—΄μ„ μ „λ‹¬ν•˜κΈ° μœ„ν•΄ @ValueSource, @NullSource, @EmptySourceλ₯Ό ν•¨κ»˜ κ²°ν•©ν•΄ μ‚¬μš©ν•  수 μžˆλ‹€.

@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = {"  ", "\t", "\n"})
void isBlank_ShouldReturnTrueForAllTypesOfBlankStrings(String input) {
    assertTrue(Strings.isBlank(input));
}

@EnumSource

μ—΄κ±°ν˜• κ°‘μ˜ 배열을 λ©”μ„œλ“œμ— μ „λ‹¬ν•œλ‹€.

@ParameterizedTest
@EnumSource(Month.class) // 12 monthsλ₯Ό 전달
void getValueForAMonth_IsAlwaysBetweenOneAndTwelve(Month month) {
    int monthNumber = month.getValue();
    assertTrue(monthNumber >= 1 && monthNumber <= 12);
}

μœ„ μ˜ˆμ‹œμ—μ„œ names 속성을 μ‚¬μš©ν•˜μ—¬ λͺ‡ 개의 λ‹¬λ§Œ 전달할 μˆ˜λ„ μžˆλ‹€.

@ParameterizedTest
@EnumSource(value = Month.class, names = {"APRIL", "JUNE", "SEPTEMBER", "NOVEMBER"})
void someMonths_Are30DaysLong(Month month) {
    final boolean isALeapYear = false;
    assertEquals(30, month.length(isALeapYear));
}

@ValueSource 와 λΉ„μŠ·ν•˜κ²Œ ν…ŒμŠ€νŠΈ μ‹€ν–‰ λ‹Ή ν•˜λ‚˜μ˜ λ§€κ°œλ³€μˆ˜λ§Œ 전달할 수 μžˆλ‹€.

@BeforeEach / @AfterEach

λ©”μ„œλ“œκ°€ ν˜„μž¬ ν…ŒμŠ€νŠΈ 클래슀의 각각의 λ©”μ„œλ“œλ³΄λ‹€ λ¨Όμ € / 각각의 λ©”μ„œλ“œμ˜ 이후에 μ‹€ν–‰ν•œλ‹€.

  • 리턴 νƒ€μž… : void
  • private λΆˆκ°€λŠ₯
  • static λΆˆκ°€λŠ₯
    @BeforeEach
    void setUp(){
      numbers = new HashSet<>();
      numbers.add(1);
      numbers.add(2);
      numbers.add(3);
      numbers.add(4);
    }
    

@BeforeAll / @AfterAll

λ©”μ„œλ“œκ°€ ν˜„μž¬ ν…ŒμŠ€νŠΈ 클래슀의 λͺ¨λ“  λ©”μ„œλ“œλ³΄λ‹€ λ¨Όμ € / λͺ¨λ“  λ©”μ„œλ“œμ˜ 이후에 μ‹€ν–‰ν•œλ‹€.

@DisplayName

클래슀 λ˜λŠ” λ©”μ„œλ“œμ— λŒ€ν•œ μ‚¬μš©μž 지정 ν‘œμ‹œ 이름을 μ„ μ–Έν•˜λŠ”λ° μ‚¬μš©ν•œλ‹€.

@DisplayName("Set의 μ‚¬μ΄μ¦ˆ 확인 ν…ŒμŠ€νŠΈ")
@Test
void checkSizeOfSet(){
    assertThat(numbers.size()).isEqualTo(4);
}

@Disabled

클래슀 λ˜λŠ” λ©”μ„œλ“œλ₯Ό λΉ„ν™œμ„±ν™” ν•˜λŠ”λ° μ‚¬μš©ν•œλ‹€.

참고 자료