[Kotlin] JUnit5

πŸ’‘ ν…ŒμŠ€νŠΈ ν”„λ ˆμž„ μ›Œν¬μΈ JUnit5λ₯Ό ν™œμš©ν•΄μ„œ λ‹¨μœ„ ν…ŒμŠ€νŠΈμ™€ 톡합 ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•˜κ³  μ‹€ν–‰ν•˜λŠ” 방법을 ν™œμš©ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

Junit5

  • μžλ°” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μœ„ν•œ μ΅œμ‹  ν…ŒμŠ€νŠΈ ν”„λ ˆμž„μ›Œν¬λ‘œ, λ‹¨μœ„ ν…ŒμŠ€νŠΈ 및 톡합 ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•˜κ³  μ‹€ν–‰ν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€.
  • Java 8μ—μ„œ λ„μž…λœ κΈ°λŠ₯을 νŠΉλ³„νžˆ νƒ€κ²ŸμœΌλ‘œ ν•˜λŠ” μ—¬λŸ¬ κΈ°λŠ₯이 ν¬ν•¨λ˜μ–΄ 있으며, 주둜 λžŒλ‹€ ν‘œν˜„μ‹€μ„ μ‚¬μš©ν•˜μ—¬ κ΅¬μΆ•λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

ꡬ성 μš”μ†Œ

  • Junit Platform
    • ν…ŒμŠ€νŠΈλ₯Ό μ‹€ν–‰ν•˜κΈ° μœ„ν•œ κΈ°λ°˜μ„ μ œκ³΅ν•©λ‹ˆλ‹€.
    • IDEλ‚˜ λΉŒλ“œ 도ꡬ(Maven, Gradle λ“±)μ™€μ˜ 톡합을 μ§€μ›ν•˜λ©°, λ‹€μ–‘ν•œ ν…ŒμŠ€νŠΈ 엔진을 μ‹€ν–‰ν•  수 μžˆλŠ” 곡톡 μ‹€ν–‰ ν™˜κ²½μ„ μ œκ³΅ν•©λ‹ˆλ‹€.
  • Junit Jupiter
    • JUnit 5μ—μ„œ μƒˆλ‘­κ²Œ λ„μž…λœ ν”„λ‘œκ·Έλž˜λ° 및 ν™•μž₯ λͺ¨λΈμ„ μ œκ³΅ν•©λ‹ˆλ‹€.
    • @Tset, @BeforEath, @AfterEach λ“± μƒˆλ‘œμš΄ μ–΄λ…Έν…Œμ΄μ…˜μ΄ ν¬ν•¨λ˜μ–΄ 있으며, JUnit5μ—μ„œ 주둜 μ‚¬μš©λ˜λŠ” API μž…λ‹ˆλ‹€.
  • JUnit Vintage
    • JUnit 4와 이전 λ²„μ „μ˜ ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό JUnit5 ν™˜κ²½μ—μ„œ μ‹€ν–‰ν•  수 μžˆλ„λ‘ μ§€μ›ν•˜λŠ” λͺ¨λ“ˆμž…λ‹ˆλ‹€.
    • 기쑴의 JUnit4 ν…ŒμŠ€νŠΈλ₯Ό λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ ν•  ν•„μš” 없이 μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ£Όμš” μ–΄λ…Έν…Œμ΄μ…˜

  • Junit5μ—μ„œλŠ” ν…ŒμŠ€νŠΈ λ©”μ„œλ“œλ₯Ό μ •μ˜ν•˜κ³ , ν…ŒμŠ€νŠΈ 수λͺ…μ£ΌκΈ°λ₯Ό κ΄€λ¦¬ν•˜κΈ° μœ„ν•΄ λ‹€μ–‘ν•œ μ–΄λ…Έν…Œμ΄μ…˜μ„ μ œκ³΅ν•©λ‹ˆλ‹€.
    • @Test : ν…ŒμŠ€νŠΈ λ©”μ„œλ“œλ₯Ό μ •μ˜ν•˜λ©°, ν•΄λ‹Ή λ©”μ„œλ“œλŠ” ν…ŒμŠ€νŠΈ 엔진에 μ˜ν•΄ μ‹€ν–‰λ©λ‹ˆλ‹€.
    • @BeforeEach : 각 ν…ŒμŠ€νŠΈ λ©”μ„œλ“œκ°€ μ‹€ν–‰λ˜κΈ° 전에 μ‹€ν–‰λ˜λŠ” λ©”μ„œλ“œλ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.
    • @AfterEach : 각 ν…ŒμŠ€νŠΈ λ©”μ„œλ“œκ°€ μ‹€ν–‰λœ 후에 μ‹€ν–‰λ˜λŠ” λ©”μ„œλ“œλ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.
    • @BeforAll : λͺ¨λ“  ν…ŒμŠ€νŠΈκ°€ μ‹€ν–‰λ˜κΈ° 전에 ν•œ 번만 μ‹€ν–‰λ˜λŠ” λ©”μ„œλ“œλ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.
    • @AfterAll : λͺ¨λ“  ν…ŒμŠ€νŠΈκ°€ μ‹€ν–‰ 된 후에 ν•œ 번만 μ‹€ν–‰λ˜λŠ” λ©”μ„œλ“œλ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.
    • @DisplyName : ν…ŒμŠ€νŠΈ λ©”μ„œλ“œμ— λŒ€ν•œ μ‚¬μš©μž μ •μ˜ 이름을 μ§€μ •ν•©λ‹ˆλ‹€.
    • @Disabled : νŠΉμ • ν…ŒμŠ€νŠΈ λ©”μ„œλ“œλ₯Ό λΉ„ν™œμ„±ν™”ν•˜κ±°λ‚˜ λ¬΄μ‹œν•  λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.

κ΅¬ν˜„ν•΄λ³΄κΈ°

  • Junit5λ₯Ό ν™œμš©ν•œ κ°„λ‹¨ν•œ ν…ŒμŠ€νŠΈλ₯Ό κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€.
import org.junit.jupiter.api.*

// @BeforeAll, @AfterAll λ©”μ„œλ“œλ₯Ό μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œλ‘œ μ‚¬μš© κ°€λŠ₯ν•˜κ²Œ μ„€μ •
@TestInstance(TestInstance.Lifecycle.PER_CLASS) 
class CalculatorTest {

    private lateinit var calculator: Calculator

    @BeforeAll
    fun initAll() {
        println("Before all tests")
    }

    @BeforeEach
    fun setUp() {
        println("Before each test")
        calculator = Calculator()
    }

    @Test
    @DisplayName("Test for addition")
    fun testAddition() {
        println("Testing addition")
        Assertions.assertEquals(4, calculator.add(2, 2))
    }

    @Test
    @DisplayName("Test for subtraction")
    fun testSubtraction() {
        println("Testing subtraction")
        Assertions.assertEquals(0, calculator.subtract(2, 2))
    }

    @Test
    @DisplayName("Test for multiplication")
    // Disableλ₯Ό ν†΅ν•΄μ„œ ν…ŒμŠ€νŠΈλ₯Ό μ‹€ν–‰ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
    @Disabled("Disabled until bug #1234 has been fixed") 
    fun testMultiplication() {
        println("Testing multiplication")
        Assertions.assertEquals(4, calculator.multiply(2, 2))
    }

    @AfterEach
    fun tearDown() {
        println("After each test")
    }

    @AfterAll
    fun tearDownAll() {
        println("After all tests")
    }
}

class Calculator {
    fun add(a: Int, b: Int): Int = a + b
    fun subtract(a: Int, b: Int): Int = a - b
    fun multiply(a: Int, b: Int): Int = a * b
}

Advanced Assertions

  • JUnit5λŠ” λžŒλ‹€λ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄ μ—¬λŸ¬ μƒμœ„ μ–΄μ„€μ…˜μ„ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Exception Asserting

  • 호좜이 μ˜ˆμ™Έλ₯Ό throwν•  κ²ƒμœΌλ‘œ μ˜ˆμƒλ˜λŠ” κ²½μš°μ— μ–΄μ„€μ…˜μ„ μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • νŠΉμ • 호좜이 μ˜ˆμƒλœ μ˜ˆμ™Έλ₯Ό throwν•˜λŠ”μ§€ ν…ŒμŠ€νŠΈν•  수 μžˆμŠ΅λ‹ˆλ‹€.
@Test
fun `Dividing by zero should throw the DivideByZeroException`() {
    val exception = Assertions.assertThrows(DivideByZeroException::class.java) {
        calculator.divide(5, 0)
    }

    Assertions.assertEquals(5, exception.numerator)
}

Multiple Assertions

  • μ—¬λŸ¬ 개의 μ–΄μ„€μ…˜μ„ λ™μ‹œμ— μˆ˜ν–‰ν•  수 μžˆλŠ” κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€.
  • λͺ¨λ“  κΈ°λŠ₯을 ν‰κ°€ν•˜λ©°, λͺ¨λ“  μ‹€νŒ¨μ— λŒ€ν•΄ λ³΄κ³ ν•©λ‹ˆλ‹€.
fun `The square of a number should be equal to that number multiplied in itself`() {
    Assertions.assertAll(
        Executable { Assertions.assertEquals(1, calculator.square(1)) },
        Executable { Assertions.assertEquals(4, calculator.square(2)) },
        Executable { Assertions.assertEquals(9, calculator.square(3)) }
    )
}

Boolean Assertions

  • 호좜이 μ°Έ or 거짓을 μ •μƒμ μœΌλ‘œ λ°˜ν™˜ν•˜λŠ”μ§€ ν…ŒμŠ€νŠΈν•  수 μžˆμŠ΅λ‹ˆλ‹€.
@Test
fun `isEmpty should return true for empty lists`() {
    val list = listOf<String>()
    Assertions.assertTrue(list::isEmpty)
}

JUnit5의 μž₯점

  • μœ μ—°ν•œ ν…ŒμŠ€νŠΈ λͺ¨λΈ
    • ν…ŒμŠ€νŠΈ μ‹€ν–‰μ˜ μœ μ—°μ„±μ„ 높이기 μœ„ν•΄ λ‹€μ–‘ν•œ ν™•μž₯ 포인트λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.
    • ν…ŒμŠ€νŠΈ 라이프 사이클, 쑰건뢀 μ‹€ν–‰, 반볡 ν…ŒμŠ€νŠΈ λ“±..
  • ν™•μž₯ κ°€λŠ₯μ„±
    • μ‚¬μš©μž μ •μ˜ ν™•μž₯을 톡해 ν”„λ ˆμž„μ›Œν¬λ₯Ό ν™•μž₯ν•  수 μžˆλŠ” κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€.
  • ν¬ν˜„λ ₯이 높은 ν…ŒμŠ€νŠΈ
    • @DisplyName을 μ‚¬μš©ν•΄ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€μ— λͺ…ν™•ν•˜κ³  직관적인 이름을 λΆ€μ—¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • 더 λ‚˜μ€ ꡬ쑰화
    • JUnit Jupiter, JUnit Playform, JUnit Vintage와 같은 λͺ¨λ“ˆν™”λœ ꡬ쑰λ₯Ό 톡해 ν™•μž₯ κ°€λŠ₯ν•˜κ³  더 λ‚˜μ€ ꡬ쑰화λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

κ²°λ‘ 

  • JUnit 5λŠ” μžλ°” μ• ν”Œλ¦¬μΌ€μ΄μ…˜ ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•œ μ΅œμ‹  ν‘œμ€€ ν”„λ ˆμž„μ›Œν¬μž…λ‹ˆλ‹€.
  • μœ μ—°ν•˜κ³  ν™•μž₯ κ°€λŠ₯ν•œ ν…ŒμŠ€νŠΈ ν™˜κ²½μ„ μ œκ³΅ν•˜λ©°, λ‹€μ–‘ν•œ ν…ŒμŠ€νŠΈ μ „λž΅μ„ μ§€μ›ν•©λ‹ˆλ‹€.
  • JUnit 5λ₯Ό μ‚¬μš©ν•˜μ—¬ ν…ŒμŠ€νŠΈ μ½”λ“œμ˜ ν’ˆμ§ˆμ„ 높이고, μ½”λ“œμ˜ μ•ˆμ •μ„±κ³Ό μœ μ§€λ³΄μˆ˜μ„±μ„ κ°œμ„ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ°Έκ³ 

JUnit 5 for Kotlin Developers | Baeldung on Kotlin