본문 바로가기
Group Study (22-23)/TDD

[TDD] 4. JUnit 5 기초

by chaerlo127 2023. 1. 27.

작성자 : 장채은

 

실습 환경

- Java 11

- Spring Boot 2.7.7

 

 

🍰 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

 


 

https://github.com/GDSC-MYONGJI/22-23-TDD-Study

 

GitHub - GDSC-MYONGJI/22-23-TDD-Study: [22-23 GDSC MJU 1기] TDD 스터디를 위한 저장소

[22-23 GDSC MJU 1기] TDD 스터디를 위한 저장소. Contribute to GDSC-MYONGJI/22-23-TDD-Study development by creating an account on GitHub.

github.com

참고용 도서

http://www.yes24.com/Product/Goods/89145195

 

테스트 주도 개발 시작하기 - YES24

TDD(Test-Driven Development)는 테스트부터 시작한다. 구현을 먼저 하고 나중에 테스트하는 것이 아니라 먼저 테스트를 하고 그다음에 구현한다. 구현 코드가 없는데 어떻게 테스트할 수 있을까? 여기

www.yes24.com

 

'Group Study (22-23) > TDD' 카테고리의 다른 글

[TDD] 6. 대역  (1) 2023.02.24
[TDD] 5. 테스트 코드의 구성  (1) 2023.02.02
[TDD] 3. TDD ∙ 기능 명세 ∙ 설계  (3) 2023.01.21
[TDD] 2. 테스트 코드 작성 순서  (1) 2023.01.17
[TDD] 1. TDD 시작  (4) 2023.01.06