JUnit & AssertJ

@WonyuΒ· March 06, 2025 Β· 6 min read

πŸ“Œ JUnitμ΄λž€?

βœ… Java μ§„μ˜μ—μ„œ κ°€μž₯ 많이 μ‚¬μš©λ˜λŠ” μœ λ‹› ν…ŒμŠ€νŠΈ ν”„λ ˆμž„μ›Œν¬
βœ… Java μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ μœ λ‹› ν…ŒμŠ€νŠΈμ™€ 톡합 ν…ŒμŠ€νŠΈλ₯Ό μ‹€ν–‰ν•˜λŠ” 데 μ‚¬μš©

κ΅¬μ„±μš”μ†Œ

JUnit 5λŠ” λ‹€μŒ 3κ°€μ§€ λͺ¨λ“ˆλ‘œ κ΅¬μ„±λœλ‹€.

λͺ¨λ“ˆ μ„€λͺ…
JUnit Platform λ‹€μ–‘ν•œ ν…ŒμŠ€νŠΈ 엔진을 μ‹€ν–‰ν•  수 μžˆλŠ” 기반 제곡
JUnit Jupiter JUnit 5의 ν…ŒμŠ€νŠΈ μ—”μ§„ 제곡 (@Test, @BeforeEach λ“±)
JUnit Vintage JUnit 3, 4 기반의 ν…ŒμŠ€νŠΈ μ‹€ν–‰ 지원

πŸ“Œ JUnit 라이프사이클 μ–΄λ…Έν…Œμ΄μ…˜

βœ… ν…ŒμŠ€νŠΈ μ‹€ν–‰ μ „/ν›„ μ„€μ • 및 정리λ₯Ό μˆ˜ν–‰ν•˜λŠ” λ©”μ„œλ“œλ₯Ό μ •μ˜ν•  수 있게 ν•œλ‹€.

μ–΄λ…Έν…Œμ΄μ…˜ μ„€λͺ…
@BeforeEach 각 ν…ŒμŠ€νŠΈ 전에 μ‹€ν–‰
@AfterEach 각 ν…ŒμŠ€νŠΈ 후에 μ‹€ν–‰
@BeforeAll λͺ¨λ“  ν…ŒμŠ€νŠΈ μ‹€ν–‰ 전에 1번 μ‹€ν–‰
@AfterAll λͺ¨λ“  ν…ŒμŠ€νŠΈ μ‹€ν–‰ ν›„ 1번 μ‹€ν–‰
import org.junit.jupiter.api.*;

class LifecycleTest {
    
    @BeforeAll
    static void beforeAll() {
        System.out.println("πŸ“Œ λͺ¨λ“  ν…ŒμŠ€νŠΈ μ‹œμž‘ μ „ (1회 μ‹€ν–‰)");
    }

    @BeforeEach
    void beforeEach() {
        System.out.println("πŸ”Ή κ°œλ³„ ν…ŒμŠ€νŠΈ μ‹€ν–‰ μ „");
    }

    @Test
    void test1() {
        System.out.println("βœ… μ‹€ν–‰: test1");
    }

    @Test
    void test2() {
        System.out.println("βœ… μ‹€ν–‰: test2");
    }

    @AfterEach
    void afterEach() {
        System.out.println("πŸ”Ή κ°œλ³„ ν…ŒμŠ€νŠΈ μ‹€ν–‰ ν›„");
    }

    @AfterAll
    static void afterAll() {
        System.out.println("πŸ“Œ λͺ¨λ“  ν…ŒμŠ€νŠΈ μ’…λ£Œ ν›„ (1회 μ‹€ν–‰)");
    }
}

πŸ“Œ JUnit 단언 (Assertions)

βœ… ν…ŒμŠ€νŠΈ κ²°κ³Όλ₯Ό κ²€μ¦ν•˜λŠ” λ‹€μ–‘ν•œ Assertionsλ₯Ό 제곡
βœ… λͺ¨λ‘ org.junit.jupiter.api.Assertions 클래슀의 static λ©”μ„œλ“œλ‘œ 제곡

λ©”μ„œλ“œ μ„€λͺ…
assertEquals(expected, actual) κΈ°λŒ€κ°’κ³Ό μ‹€μ œκ°’ 비ꡐ
assertTrue(condition) 쑰건이 true인지 확인
assertFalse(condition) 쑰건이 false인지 확인
assertNotNull(object) 객체가 null이 μ•„λ‹˜μ„ 확인
assertThrows(Exception.class, () -> {...}) μ˜ˆμ™Έ λ°œμƒ μ—¬λΆ€ 확인

Assertions μ˜ˆμ‹œ

@Test  
void addAssertions() {  
    int sum = 2 + 3;  
    Assertions.assertEquals(sum, 5);  
}

μ˜ˆμ™Έ 처리 Assertions 예제

@Test  
void exceptionAssertions() {  
    Assertions.assertThrows(ArithmeticException.class, () -> {  
        int result = 1 / 0;  
    });  
}

문제점

❌ 가독성이 λ–¨μ–΄μ§€λŠ” 검증
❌ λ©”μ„œλ“œ 체이닝을 μ§€μ›ν•˜μ§€ μ•Šμ•„ μ—¬λŸ¬ 검증을 μˆ˜ν–‰ν•˜κΈ° 어렀움
❌ μ˜ˆμ™Έ 검증이 μ œν•œμ 

AssertJλŠ” μœ„μ™€ 같은 JUnit의 λ¬Έμ œμ μ„ ν•΄κ²°ν•œλ‹€.


πŸ“Œ AssertJλž€?

βœ… Javaμ—μ„œ ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•  λ•Œ ν•„μš”ν•œ 직관적인 단언을 μ œκ³΅ν•˜λŠ” 라이브러리
βœ… ν…ŒμŠ€νŠΈ μ½”λ“œμ˜ 가독성을 ν–₯상

κ΅¬μ„±μš”μ†Œ

βœ… μ—¬λŸ¬ λͺ¨λ“ˆλ‘œ κ΅¬μ„±λ˜μ–΄ 있으며, 각 λͺ¨λ“ˆμ€ νŠΉμ •ν•œ νƒ€μž…μ— λŒ€ν•œ 단언을 제곡

  • AssertJ Core: JDK νƒ€μž…(String, Iterable, Stream, Path, File, Map λ“±)에 λŒ€ν•œ 단언을 제곡
  • AssertJ Guava: Guava 라이브러리의 νƒ€μž…(Multimap, Optional λ“±)에 λŒ€ν•œ 단언을 제곡
  • AssertJ Joda-Time: Joda-Time 라이브러리의 νƒ€μž…(DateTime, LocalDateTime λ“±)에 λŒ€ν•œ 단언을 제곡
  • AssertJ Neo4j: Neo4j λ°μ΄ν„°λ² μ΄μŠ€μ˜ νƒ€μž…(Path, Node, Relationship λ“±)에 λŒ€ν•œ 단언을 제곡
  • AssertJ DB: κ΄€κ³„ν˜• λ°μ΄ν„°λ² μ΄μŠ€μ˜ νƒ€μž…(Table, Row, Column λ“±)에 λŒ€ν•œ 단언을 제곡
  • AssertJ Swing: Swing μ‚¬μš©μž μΈν„°νŽ˜μ΄μŠ€μ˜ κΈ°λŠ₯적 ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•œ APIλ₯Ό 제곡

⭐️ AssertJ Coreκ°€ κ°€μž₯ 널리 μ‚¬μš©λ˜λ©°, λ‚˜λ¨Έμ§€ λͺ¨λ“ˆμ€ νŠΉμ • ν™˜κ²½(Guava, Joda-Time, DB λ“±)μ—μ„œ μ‚¬μš©λ¨

νŠΉμ§•

  • ν–₯μƒλœ 가독성: assertThat() λ©”μ„œλ“œλ₯Ό μ€‘μ‹¬μœΌλ‘œ μžμ—°μŠ€λŸ¬μš΄ μ–Έμ–΄ ꡬ쑰λ₯Ό 제곡
  • ν’λΆ€ν•œ 단언 λ©”μ„œλ“œ: λ¬Έμžμ—΄, μ»¬λ ‰μ…˜, μ˜ˆμ™Έ, 파일 λ“± λ‹€μ–‘ν•œ νƒ€μž…μ— λŒ€ν•œ 단언을 지원
  • λ©”μ„œλ“œ 체이닝: μ—¬λŸ¬ 단언을 μ²΄μ΄λ‹ν•˜μ—¬ 가독성 ν–₯상
  • IDE μΉœν™”μ : μ½”λ“œ μžλ™ μ™„μ„± κΈ°λŠ₯을 톡해 개발 생산성을 ν–₯상

πŸ“Œ AssertJ 단언 (Assertions)

κΈ°λ³Έ 단언

@Test
void basicAssertions() {
    String name = "Frodo";
    assertThat(name).isEqualTo("Frodo")
                    .startsWith("Fro")
                    .endsWith("do")
                    .isEqualToIgnoringCase("frodo");
}

μ»¬λ ‰μ…˜ 단언

@Test  
void collectionAssertions() {  
    List<String> fellowship = List.of("Frodo", "Sam", "Gandalf");  
    assertThat(fellowship).hasSize(3)  
                          .contains("Frodo", "Sam")  
                          .doesNotContain("Sauron");  
}

μ˜ˆμ™Έ 단언

import static org.assertj.core.api.Assertions.*;

@Test  
void exceptionAssertions() {  
    assertThatThrownBy(() -> {  
        throw new Exception("boom!");  
    }).isInstanceOf(Exception.class)  
      .hasMessageContaining("boom");  
}

객체 단언

public class Member {
	Long id;
	String name;
	String nickname;
}

@Test  
void fieldsAssertions() {  
    Member member1 = new Member(1L, "choi", "phillip");  
    Member member2 = new Member(null, "choi", "phillip");  
  
    assertThat(member1).usingRecursiveComparison()  
                       .ignoringFields("id")  
                       .isEqualTo(member2);  
}

πŸ“Œ JUnitκ³Ό AssertJ의 관계

  • JUnit은 ν…ŒμŠ€νŠΈ 싀행을 λ‹΄λ‹Ή (ν…ŒμŠ€νŠΈ 라이프사이클 관리, @Test 제곡)
  • AssertJλŠ” 직관적이고 κ°•λ ₯ν•œ 검증을 λ‹΄λ‹Ή (assertThat()으둜 가독성 κ°œμ„ )
  • AssertJλŠ” JUnit이 μ—†μœΌλ©΄ ν…ŒμŠ€νŠΈλ₯Ό μ‹€ν–‰ν•  수 μ—†μŒ
  • JUnit만 μ‚¬μš©ν•˜λ©΄ 검증 μ½”λ“œκ°€ λΆˆνŽΈν•˜λ―€λ‘œ, AssertJλ₯Ό μΆ”κ°€ν•˜λ©΄ 가독성이 ν–₯상됨

πŸ“Œ μ°Έκ³ 

https://junit.org/junit5/docs/current/user-guide
https://assertj.github.io/doc

@Wonyu
Hello :) I'm Wonyu