JUnit Test

danbi lee·2024년 12월 19일

JUnit5를 이용해 테스트 코드 작성하기

테스트 코드란

프로그래밍 코드를 사용해 자동으로 기능을 검증하는 것을 말한다.

장점

  1. 개발 과정에서 문제를 미리 발견할 수 있다.
  2. 기능 추가와 리팩토링을 안심하고 할 수 있다.
  3. 빠른 시간 내 코드의 동작 방식과 결과를 확인할 수 있다.
  4. 좋은 테스트 코드를 작성하려 하다보면, 자연스럽게 좋은 코드가 만들어진다.
  5. 잘 작성한 테스트는 문서 역할을 한다.

수동 테스트 단점

테스트 코드는 수동으로 만들 수 있지만 다음과 같은 단점이 있다.

  1. 복잡한 로직이나 다양한 경로를 테스트해야 할 경우 작성해야 하는 코드량이 많아진다. 메인 메소드가 아주 커지고 테스트 메소드를 개별적으로 실행하기 어렵다.
  2. 코드가 변경될 때마다 수동으로 테스트 코드를 모두 업데이트 해야한다.
  3. 동일하거나 유사한 테스트 시나리오를 반복적으로 작성해야 하는 경우가 많다.
  4. 테스트가 실패한 경우 기대값과 들어온값을 알려주지 않는다.

이러한 단점을 보완하기 위해 JUnit같은 테스트 자동화 도구를 활용한다.

세팅

plugins {
    id 'org.jetbrains.kotlin.jvm' version '2.1.0'
    id 'java'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    
    // JUnit 5 의존성
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.1'
    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.1'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.1'
    
    // Spring Boot Test (이미 포함하고 계신)
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    
    // Mockito Kotlin (모킹이 필요한 경우)
    testImplementation 'org.mockito.kotlin:mockito-kotlin:5.2.1'
    
    // AssertJ (더 직관적인 검증을 위해 권장)
    testImplementation 'org.assertj:assertj-core:3.24.2'
}

tasks.named('test') {
    useJUnitPlatform() // JUnit 5 사용 설정
}

compileKotlin {
    kotlinOptions {
        jvmTarget = "21"
    }
}

compileTestKotlin {
    kotlinOptions {
        jvmTarget = "21"
    }
}

테스트 코드 작성

1. 패키지 생성

src/main/kotlin과 동일하게 src/test에 kotlin 디렉토리를 만들고, 패키지 구조를 맞춰준다.

2. 클래스 생성

테스트 클래스는 test 디렉토리내 같은 패키지를 사용하고, 테스트 대상 클래스 뒤에 Test라는 이름을 붙이는 것이 관례다.

  • 모든 계층에 대한 많은 경우를 검증하는게 좋으나 일반적으로는 Service 계층을 테스트한다.
  • @SpringBootTest 통합 테스트를 제공하는 기본적인 스프링 부트 테스트 어노테이션으로, 여러 단위 테스트를 하나의 통합된 테스트로 수행
  • @Autowired constructor 생성자 앞에 @Autowired를 사용해 생성자 Bean을 주입받게 함
@SpringBootTest
class BookServiceTest @Autowired constructor(
    private val bookService: BookService,
    private val bookRepository: BookRepository,
    private val userRepository: UserRepository,
    private val userLoanHistoryRepository: UserLoanHistoryRepository
){

    @AfterEach
    fun clean() {
        bookRepository.deleteAll()
        userRepository.deleteAll() // deleteAll()은 연관관계의 자식 테이블까지 찾아서 삭제해준다.
    }

    @Test
    @DisplayName("책 등록이 정상 동작한다")
    fun saveBookTest() {
        // given
        val request = BookRequest("일론 머스크")

        // when
        bookService.saveBook(request)

        // then
        val books = bookRepository.findAll()
        assertThat(books).hasSize(1)
        assertThat(books[0].name).isEqualTo("일론 머스크")
    }
}

3. given - when - then 패턴

테스트 메소드는 3가지 과정으로 구정되어 있는데, 이것을 given - when - then 패턴이라고 한다.

  1. givne 테스트 대상을 만들어 준비하는 과정
  2. when 실제 테스트하고 싶은 기능을 호출하는 과정
  3. then 호출 이후 의도한대로 결과가 나왔는지 확인하는 과정

4. JUnit5 사용

  • @Test 테스트 메소드 지정
  • @BeforeEach 테스트 수행 전 실행되는 메소드 지정
  • @AfterEach 테스트 수행 후 실행되는 메소드 지정
  • @BeforeAll 모든 테스트 수행 전 최초 1회 실행되는 메소드 지정
    • 코틀린에서는 @JvmStatic을 붙여야 함
  • @AfterAll 모든 테스트 수행 후 최초 1회 실행되는 메소드 지정
    • 코틀린에서는 @JvmStatic을 붙여야 함
  • @DisplayName 테스트에 메소드에 이름 지정
  • 터미널 ./gradlew test 전체 테스트 돌리기

실전! 코틀린과 스프링 부트로 도서관리 애플리케이션 개발하기 (Java 프로젝트 리팩토링)

profile
계속해서 보완중

0개의 댓글