TIL/Kotlin

[TIL/Kotlin] Junit5로 테스트 코드 작성하기

야리니 2023. 12. 12. 20:39
728x90
반응형

1. Junit5에서 사용되는 5가지 어노테이션

1) @Test : 테스트 메소드를 지정한다. 테스트 메소드를 싱행하는 과정에서 오류가 없으면 성공

2) @BeforeEach : 각 테스트 메소드가 수행되기 전에 실행되는 메소드를 지정. 매번 실행

3) @AfterEach : 각 테스트 메소드가 수행된 후에 실행되는 메소드를 지정. 매번 실행

4) @BeforeAll : 모든 테스트를 수행하기 전에 최초 1회 수행되는 메소드를 지정

5) @AfterAll : 모든 테스트를 수행한 후 최후 1회 수행되는 메소드를 지정

 

* BeforeAll과 AfterAll은 @JvmStatic을 붙여줘야한다.

package com.group.libraryapp

import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test

class JunitTest {

    companion object {
        @BeforeAll
        @JvmStatic
        fun beforeAll() {
            println("모든 테스트 시작 전")
        }

        @AfterAll
        @JvmStatic
        fun afterAll() {
            println("모든 테스트 종료 후")
        }
    }
    
    @BeforeEach
    fun beforeEach() {
        println("각 테스트 시작 전")
    }

    @AfterEach
    fun afterEach() {
        println("각 테스트 종료 후")
    }

    @Test
    fun test1() {
        println("테스트 1")
    }

    @Test
    fun test2() {
        println("테스트 2")
    }
}

 

실행 결과


2. 단언문

  • assertThat(확인하고 싶은 값)
  • .isEqualTo(기대하는 값)
  • .isTrue / .isFalse 주어진 값이 true인지 false인지 검증
  • .hasSize(기대하는 사이즈)
  • .extracting(name).containsExactlyInAnyOrder("A","B") : 해당하는 리스트들의 name만 가져와서 그 값이 정확히 A인지 B인지 검증. 순서는 검증하지 않기 때문에 순서는 중요하지 않음
  • .containsExactly("A", "B") : 순서까지 검증
  • assertThrows<IllegalArgumentException> { 
        function1() 
    } : function1 함수를 실행했을 때 IllegalArgumentException이 나오는지 검증
  • val message = assertThrows<IllegalArgumentException> { 
        function1() 
    }.message
    assertThat(message).isEqualTo("잘못된 값이 들어왔습니다") : message를 가져와 예외 메시지를 확인할 수 있음

3. Spring Boot 각 계층을 테스트 하는 방법

- Domain 계층 : 클래스를 테스트 하는 것과 동일

- Service, Repository 계층 : 스프링 빈을 사용하는 테스트 방법 사용 (@SpringBootTest), 데이터 위주의 검증

- Controller 계층 : 스프링 빈을 사용하는 테스트 방법 사용 (@SpringBootTest), 응답 받은 JSON을 비롯한 HTTP 위주의 검증

 

딱 1개의 계층만 테스트 한다면 보통은 Service 계층을 테스트

A를 보냈을 때 B가 잘 나오는지, 원하는 로직을 잘 수행하는지 검증 가능


4. 테스트 코드 작성

package com.group.libraryapp.service.user

import com.group.libraryapp.domain.user.User
import com.group.libraryapp.domain.user.UserRepository
import com.group.libraryapp.dto.user.request.UserCreateRequest
import com.group.libraryapp.dto.user.request.UserUpdateRequest
import org.assertj.core.api.AssertionsForInterfaceTypes.assertThat
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest
// @Autowired constructor를 붙이면 각각 @Autowired를 붙이지 않아도 됨
class UserServiceTest @Autowired constructor(
    private val userRepository: UserRepository,
    private val userService: UserService,
) {

    // DB를 공유자원으로 사용하고 있기 때문에 각 테스트 코드를 실행한 뒤에 자원을 비우도록 하기 위함
    @AfterEach
    fun clean() {
        userRepository.deleteAll()
    }

    @Test
    @DisplayName("유저 저장이 정상 동작한다.")
    fun saveUserTest() {
        // given
        val request = UserCreateRequest("아무개", null)

        // when
        userService.saveUser(request)

        // then
        val results = userRepository.findAll()
        assertThat(results).hasSize(1)
        assertThat(results[0].name).isEqualTo("아무개")
        assertThat(results[0].age).isNull() // Java와 Kotlin의 자료형 차이 때문에 Exception 발생하는데 User에서 어노테이션을 붙여줘야함
    }

    @Test
    @DisplayName("유저 조회가 정상 동작한다.")
    fun getUsersTest() {
        // given
        userRepository.saveAll(listOf(
            User("A", 20),
            User("B", null)
        ))

        // when
        val results = userService.getUsers()

        // then
        assertThat(results).hasSize(2)
        assertThat(results).extracting("name").containsExactlyInAnyOrder("A", "B")
        assertThat(results).extracting("age").containsExactlyInAnyOrder(20, null)
    }

    @Test
    @DisplayName("유저 업데이트가 정상 동작한다.")
    fun updateUserNameTest() {
        // given
        val savedUser = userRepository.save(User("A", null))
        val request = UserUpdateRequest(savedUser.id, "B")

        // when
        userService.updateUserName(request)

        // then
        val result = userRepository.findAll()[0]
        assertThat(result.name).isEqualTo("B")
    }

    @Test
    @DisplayName("유저 삭제가 정상 동작한다.")
    fun deleteUserTest() {
        // given
        userRepository.save(User("A", null))

        // when
        userService.deleteUser("A")

        // then
        assertThat(userRepository.findAll()).isEmpty()
    }


}

번외)

전체 테스트 코드를 실행하려면

인텔리제이에서 터미널에 ./gradlew test 명령어로 실행을 시키거나

우측에 있는 gradle 클릭 후 Tasks > verification > test 실행을 시키면 테스트 코드 전체를 실행시킬 수 있다.

728x90
반응형