Backend Language/TDD

[TDD Chapter5] JUnit 5 기초

chaerlo127 2023. 1. 27. 01:19
728x90

🍰 JUnit 5 λͺ¨λ“ˆ ꡬ성

  • JUint ν”Œλž«νΌ
    • ν…ŒμŠ€νŒ… ν”„λ ˆμž„ μ›Œν¬λ₯Ό κ΅¬λ™ν•˜κΈ° μœ„ν•œ λŸ°μ²˜μ™€ ν…ŒμŠ€νŠΈ 엔진을 μœ„ν•œ API wprhd
  • JUnit Jupiter
    • JUnit 5λ₯Ό μœ„ν•œ ν…ŒμŠ€νŠΈ API와 μ‹€ν–‰ 엔진 ꡬ성
  • JUnit Vintage
    • JUnit 3, 4둜 μž‘μ„±λœ ν…ŒμŠ€νŠΈλ₯Ό 5 ν”Œλž«νΌμ—μ„œ μ‹€ν–‰ν•˜κΈ° μœ„ν•œ λͺ¨λ“ˆ 제곡

 

Jupiter둜 ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•˜κ³  μ‹€ν–‰ν•˜λ €λ©΄ build.gradle 파일 λ‚΄ dependencies에 μ˜μ‘΄μ„ μΆ”κ°€ν•΄μ£Όμ–΄μ•Ό ν•œλ‹€.

λ‹€μŒμ€ TDD μŠ€ν„°λ”” ν”„λ‘œμ νŠΈμ˜ build.gradle file이닀. Jupiter κ΄€λ ¨ μ˜μ‘΄μ„±μ΄ μΆ”κ°€λœ 것을 확인할 수 μžˆλ‹€.

plugins {
	id 'java'
	id 'org.springframework.boot' version '2.7.7'
	id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}

group = 'testdriven'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'

	// junit
	testImplementation('org.junit.jupiter:junit-jupiter:5.5.0')
}

tasks.named('test') {
	useJUnitPlatform()
}

 

🍰 @Test Annotation & Test Method

-  @Test Annotation

  • Test λ©”μ†Œλ“œ μœ„μ— @Test Annotation λΆ™μž„
  • not private 

- assertEqals() Method

  • μ‹€μ œ κ°’κ³Ό ν…ŒμŠ€νŠΈ 값이 같은지 비ꡐ
    @Test
    void plus(){
        int result = Calculator.plus(1, 2);
        assertEquals(3, result); // μ‹€μ œ κ°’κ³Ό 동일 ν•œμ§€ 비ꡐ
        assertEquals(5, Calculator.plus(4, 1));
    }

 

 

🍰 μ£Όμš” 단언 λ©”μ†Œλ“œ

Method explanation
assertEquals(expected, actual) μ‹€μ œ κ°’κ³Ό κΈ°λŒ€ν•˜λŠ” 값이 같은지 검사
assertNotEquals(unexpected, actual) μ‹€μ œ κ°’κ³Ό νŠΉμ • κ°’κ³Ό 같지 μ•Šμ€μ§€ 검사
assertSame(Object expected, Object actual) 두 객체가 λ™μΌν•œ 객체인지 검사
assertNotSame(Object unexpected, Object actual) 두 객체가 λ™μΌν•˜μ§€ μ•Šμ€ 객체인지 검사
assertTrue(boolean condition) 값이 true 인지 검사
assertFalse(boolean condition) 값이 false 인지 검사
assertNull(Object actual) 값이 null 인지 검사
assertNotNull(Object actual) 값이 null 이 μ•„λ‹Œμ§€ 검사
fail() ν…ŒμŠ€νŠΈ μ‹€νŒ¨ 처리
assertThrows(Class<T> expectedType,
Executable executable)
executable을 μ‹€ν–‰ν•œ 결과둜 μ§€μ •ν•œ νƒ€μž…μ˜ μ΅μ…‰μ…˜μ΄ λ°œμƒν•˜λŠ”μ§€ 검사
assertDoesNotThrow(Executable executable) executable을 μ‹€ν–‰ν•œ 결과둜 μ§€μ •ν•œ νƒ€μž…μ˜ μ΅μ…‰μ…˜μ΄ λ°œμƒν•˜μ§€ μ•ŠλŠ”μ§€ 검사
Assert λ©”μ„œλ“œλŠ” μ‹€νŒ¨ν•˜λ©΄ λ‹€μŒ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜μ§€ μ•Šκ³  λ°”λ‘œ μ΅μ…‰μ…˜ λ°œμƒ

assertAll() : μ‹€νŒ¨ν•œ Test 확인 λ©”μ†Œλ“œ


public class TestMethod {
    @Test
    void assertEqualsMethod(){
        LocalDate localDate = LocalDate.now();
        LocalDate localDate1 = LocalDate.now();
        assertEquals(localDate, localDate1);
    }

    @Test
    void assertEqualsMethod2(){
        assertEquals(3, 5/2); // μ—λŸ¬ λ°œμƒμ‹œ, 검증 μ½”λ“œ μ‹€νŒ¨λ‘œ
        assertEquals(4, 2*2); // 이 μ½”λ“œ μ‹€ν–‰ λ˜μ§€ μ•ŠμŒ
    }

    @Test
    void failMethod(){
        try{
            AuthService authService = new AuthService();
            authService.authenticate(null, null); // null 이 λ°œμƒν–ˆλŠ”λ°, μ΅μ…‰μ…˜μ΄ λ°œμƒν•˜μ§€ μ•ŠμœΌλ©΄ ν…ŒμŠ€νŠΈ μ‹€νŒ¨ (null == null)
            fail();
        }catch (IllegalArgumentException e){

        }
    }

    @Test
    void assertThrowsMethod(){
        IllegalArgumentException e = assertThrows(IllegalStateException.class,
                ()->{
                    AuthService authService = new AuthService();
                    authService.authenticate(null, null);
                });

        assertTrue(e.getMessage().contains("id"));
    }
}

 

- assertAll() 

λͺ¨λ“  검증을 μ‹€ν–‰ν•˜κ³  κ·Έ 쀑에 μ‹€νŒ¨ν•œ 것이 μžˆλŠ”μ§€ ν™•μΈν•˜κ³  싢을 λ•Œ μ‚¬μš©ν•˜λŠ” λ©”μ†Œλ“œμ΄λ‹€.

    @Test
    void assertAllMethod(){
        assertAll(
                () -> assertEquals(3, 5/2), // μ‹€νŒ¨
                () -> assertEquals(4, 2*2),
                () -> assertEquals(6, 11/2) // μ‹€νŒ¨
        );
    }
    
 /**
 *μ—λŸ¬ μ½”λ“œ
 */
 org.gradle.internal.exceptions.DefaultMultiCauseException: Multiple Failures (2 failures)
	org.opentest4j.AssertionFailedError: expected: <3> but was: <2>
	org.opentest4j.AssertionFailedError: expected: <6> but was: <5>
	at app//org.junit.jupiter.api.AssertAll.assertAll(AssertAll.java:80)
	at app//org.junit.jupiter.api.AssertAll.assertAll(AssertAll.java:44)
	at app//org.junit.jupiter.api.AssertAll.assertAll(AssertAll.java:38)
---

 

🍰 ν…ŒμŠ€νŠΈ 라이프 사이클

πŸͺ@BeforeEach Annotation And @AfterEach Annotation

JUnit μ‹€ν–‰ μˆœμ„œ

1. ν…ŒμŠ€νŠΈ λ©”μ„œλ“œλ₯΄ ν¬ν•¨ν•œ 객체 생성
2. (μ‘΄μž¬ν•˜λ©΄) @BeforeEach Annotation 이 뢙은 λ©”μ†Œλ“œ μ‹€ν–‰
3. @Test μ–΄λ…Έν…Œμ΄μ…˜μ΄ 뢙은 λ©”μ†Œλ“œ μ‹€ν–‰
4. (μ‘΄μž¬ν•˜λ©΄) @AfterEach μ–΄λ…Έν…Œμ΄μ…˜μ΄ 뢙은 λ©”μ†Œλ“œ μ‹€ν–‰

 

@BeforeEach

ν…ŒμŠ€νŠΈλ₯Ό μ‹€ν–‰ν•˜λŠ”λ° ν•„μš”ν•œ μ€€λΉ„ μž‘μ—… ν•  λ•Œ μ‚¬μš©

not private

예) ν…ŒμŠ€νŠΈμ—μ„œ μ‚¬μš©ν•  μž„μ‹œ νŒŒμΌμ„ 생성, ν…ŒμŠ€νŠΈ λ©”μ†Œλ“œ μ‚¬μš©ν•  객체 생성

 

@AfterEach

ν…ŒμŠ€νŠΈλ₯Ό μ‹€ν–‰ν•œ 후에 정리할 것이 μžˆμ„ λ•Œ μ‚¬μš©

not private

예) νƒœμŠ€νŠΈμ—μ„œ μ‚¬μš©ν•œ μž„μ‹œ νŒŒμΌμ„ μ‚­μ œ

public class LifecycleTest {
    public LifecycleTest(){
        System.out.println("new LifecycleTest");
    }

    // 각각 ν…ŒμŠ€νŠΈ ν•˜λ‚˜ 진행 μ‹œμž‘ν•  λ•Œ ν˜ΈμΆœλ˜λŠ” λ©”μ†Œλ“œ
    @BeforeEach
    void setUp(){
        System.out.println("setUp");
    }

    @Test
    void a(){
        System.out.println("A");
    }

    @Test
    void b(){
        System.out.println("B");
    }

    // 각각 ν…ŒμŠ€νŠΈ ν•˜λ‚˜ 진행 끝날 λ•Œ ν˜ΈμΆœλ˜λŠ” λ©”μ†Œλ“œ
    @AfterEach
    void tearDown(){
        System.out.println("tearDown");
    }
}

 

 

πŸͺ@BeforeAll Annotation And @AfterAll Annotation

 

ν•œ 클래슀의 λͺ¨λ“  ν…ŒμŠ€νŠΈ λ©”μ„œλ“œκ°€ μ‹€ν–‰λ˜κΈ° 전에 νŠΉμ • μž‘μ—…μ„ μˆ˜ν–‰ν•΄μ•Όν•˜λŠ” 경우


@BeforeAll

정적 λ©”μ†Œλ“œμ— μ‚¬μš©

클래슀의 λͺ¨λ“  ν…ŒμŠ€νŠΈ λ©”μ†Œλ“œλ₯Ό μ‹€ν–‰ν•˜κΈ° 전에 ν•œ 번 μ‚΄ν–‰

 

@AfterAll

정적 λ©”μ†Œλ“œμ— μ‚¬μš©

클래슀의 λͺ¨λ“  ν…ŒμŠ€νŠΈ λ©”μ†Œλ“œλ₯Ό μ‹€ν–‰ν•œ λ’€ 생성

 

🍰 ν…ŒμŠ€νŠΈ λ©”μ„œλ“œ κ°„ μ‹€ν–‰ μˆœμ„œ 의쑴과 ν•„λ“œ κ³΅μœ ν•˜μ§€ μ•ŠκΈ°

public class BadTest {
    private FileOperator op = new FileOperator();
    private static File file;

    @Test
    void fileCreationTest(){
        File createdFile = op.createFile(); // 파일 생성
        assertTrue(createdFile.length()>0);
        this.file = createdFile;
    }

    @Test
    void readFileTest(){
        long data = op.readData(file); // fileCreationTestμ—μ„œ μƒμ„±ν•œ 파일 μ‚¬μš©
        assertTrue(data>0);
    }
}

 

λ‹€μŒ μœ„μ˜ μ½”λ“œλŠ” ν•˜λ‚˜μ˜ File을 두 Test μ½”λ“œκ°€ κ³΅μœ ν•˜κ³  μžˆλ‹€. 

 

μœ„μ—μ„œλΆ€ν„° fileCreationTest -> readFileTest 순으둜 μ½”λ“œκ°€ μ§„ν–‰λ˜λ©΄ ν…ŒμŠ€νŠΈμ— μ„±κ³΅ν•˜μ§€λ§Œ, ν…ŒμŠ€νŠΈ λ©”μ†Œλ“œκ°€ νŠΉμ • μˆœμ„œλ‘œ μ‹€ν–‰λœλ‹€λŠ” κ°€μ •ν•˜μ— ν…ŒμŠ€νŠΈ λ©”μ†Œλ“œλ₯Ό μž‘μ„±ν•˜λ©΄ μ•ˆλœλ‹€.

[μˆœμ„œλŠ” 버전에 따라 λ‹¬λΌμ§ˆ 수 있기 λ•Œλ¬Έμ΄λ‹€.]

 

λ”°λΌμ„œ 각 ν…ŒμŠ€νŠΈ λ©”μ†Œλ“œλŠ” μ„œλ‘œ λ…λ¦½μ μœΌλ‘œ λ™μž‘ν•΄μ•Ό ν•œλ‹€. ν•œ λ©”μ†Œλ“œμ˜ 결과에 따라 λ‹€λ₯Έ λ©”μ†Œλ“œμ˜ μ‹€ν–‰ κ²°κ³Όκ°€ 달라지면 μ•ˆλœλ‹€. 

 

 

🍰 @DisplayName, @Disabled

@DisplayName

μ΄λ¦„λ§ŒμœΌλ‘œ ν…ŒμŠ€νŠΈ λ‚΄μš©μ„ μ„€λͺ…ν•˜κΈ° λΆ€μ‘±ν•  λ•Œ μ‚¬μš©

곡백, 특수 문자 μ‚¬μš© λ“±

ν…ŒμŠ€νŠΈ κ²°κ³Όλ₯Ό λ©”μ†Œλ“œ 이름이 μ•„λ‹Œ DisplayName으둜 ν‘œμ‹œ

 

@Disabled

ν…ŒμŠ€νŠΈ λ©”μ†Œλ“œλ₯Ό μ‹€ν–‰ λŒ€μƒμ—μ„œ μ œμ™Έν•  λ•Œ μ‚¬μš©

아직 μ™„μ„±λ˜μ§€ μ•Šμ•˜κ±°λ‚˜ μž μ‹œλ™μ•ˆ ν…ŒμŠ€νŠΈλ₯Ό μ‹€ν–‰ν•˜μ§€ 말아야 ν•  λ•Œ

 

@DisplayName(("@DisplayName ν…ŒμŠ€νŠΈ"))
public class DisplayNameTest {
    @DisplayName("값이 같은지 비ꡐ")
    @Test
    void assertEqualsMethod(){

    }

    @DisplayName("μ΅μ…‰μ…˜ λ°œμƒ μ—¬λΆ€ ν…ŒμŠ€νŠΈ") // ν…ŒμŠ€νŠΈ λ©”λͺ¨
    @Test
    void assertThrowsTest(){

    }

    @Disabled // νŠΉμ • ν…ŒμŠ€νŠΈ μ‹€ν–‰ν•˜κ³  싢지 μ•Šμ„ λ•Œ
    @Test
    void failMethod(){

    }
}

 

 

🍰 λͺ¨λ“  ν…ŒμŠ€νŠΈ μ‹€ν–‰ν•˜κΈ°

배포 μ „, λͺ¨λ“  ν…ŒμŠ€νŠΈλ₯Ό μ‹€ν–‰ν•˜μ—¬ κΉ¨μ§€λŠ” ν…ŒμŠ€νŠΈκ°€ μ—†λŠ”μ§€ 확인해야 ν•œλ‹€. 
이 λ•Œ, μ‹€νŒ¨ν•˜λŠ” ν…ŒμŠ€νŠΈκ°€ μ‘΄μž¬ν•˜λ©΄ 잘λͺ»λ˜μ—ˆλ‹€λŠ” κ²ƒμœΌλ‘œ μ½”λ“œ ν‘Έμ‹œλ‚˜ 배포λ₯Ό μž μ‹œ λ©ˆμΆ”κ³  문제 원인을 μ°Ύμ•„ μ œκ±°ν•œλ‹€.
# maven test
mvn test

# gradle test
gradle test

 

 

728x90