코틀린의 테스트 코드

 

 

개요

우아한 테크코스 3주차 미션을 진행하면서 단위 테스트에 대하여 학습하게 되었습니다.

kotlin은 프로그래밍 언어 중에 간결한 문법과 테스트를 지원한다는 장점을 가지고 있습니다.

이번 미션을 통해서 JunitassertJ를 활용한 단위 테스트를 학습하고 적용하게 되었습니다.

IntelliJ 작업 환경에서 간단하게 테스트 하는 방법을 작성하였습니다.

 

테스트 코드의 장점

코틀린에서 테스트 코드를 활용하면 자동화된 테스트를 통해서 개발자가 수동으로 테스트해야 하는 시간을 절약할 수 있습니다.

초기에 버그를 발견하고, 빠른 수정을 통해서 소프트웨어 품질을 향상시킬 수 있으며,

코드 변경이나 리팩토링 후 테스트를 자동으로 실행하여 기존 기능이 제대로 작동하는지 확인할 수 있습니다.

테스트 코드를 작성함으로써 코드의 문제점이나 개선 부분을 사전에 파악할 수 있다는 장점이 있습니다.

 

직접 테스트 코드를 적용해보니 버그를 신속하게 발견하고 수정할 수 있으며,

단순히 테스트를 하는 용도가 아닌 추가적인 기능이나 예외를 작성한다는 장점을 얻을 수 있었습니다.

 

JUnit 활용

JUnit 은 자바에서 가장 널리 사용되는 테스트 프레임워크 입니다.

코틀린에서는 자바와 같이 JUnit을 활용한 테스트 코드를 작성할 수 있습니다.

 

    @Test
    fun `사용자 입력이 로또 번호가 아니라면 예외 발생`() {
        val input = "100"
        assertThrows<IllegalArgumentException> {
            inputValidation.checkIsLottoNumber(input)
        }
    }
    
    @Test
    fun `로또 번호에 중복 값이 있다면 예외 발생`() {
        val input = listOf(1, 2, 3, 4, 5, 5)
        assertThrows<IllegalArgumentException> {
            inputValidation.checkDuplication(input)
        }
    }

 

이전 프로젝트에서 위와 같이 JUnit를 활용한 테스크 코드를 작성하였습니다.

값을 검증하기 위해서 assertThrows를 활용하였는데, 예외 상황이 발생해야 테스트가 통과하게 됩니다.

 

아래와 같이 라이브러리를 import 해야 사용할 수 있습니다.

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows

 

 

assertJ 활용

assertJ는 Junit 테스트 코드에 사용되어 가독성과 편의성을 높여주는 라이브러리입니다.

함수를 연쇄적으로 호출하여 직관적인 코드를 작성할 수 있다는 특징이 있습니다.

 

주로 값을 검증하는 assertThat을 활용해서 테스트 코드를 작성합니다.

import org.assertj.core.api.Assertions.assertThat

 

JUnit에서 assertThrows를 활용한 것과는 반대로, 예외 상황이 발생하지 않아야 테스트를 통과하게 됩니다.

    @Test
    fun `사용자 입력이 로또 번호(1~45 사이 수)에 대한 유효성 검사`() {
        val input = "45"
        assertThat(inputValidation.checkIsLottoNumber(input))
    }
    
    @Test
    fun `로또 번호에 중복 값에 대한 유효성 검사`() {
        val input = listOf(1, 2, 3, 4, 5, 6)
        assertThat(inputValidation.checkDuplication(input))
    }

 

 

도메인 로직의 테스트 코드

도메인 로직이란 입출력과 같은 UI 로직이 아닌 비즈니스에 중요한 결정을 내리는 역할을 하는 기능을 말합니다.

주어진 문제나 업무 영역에서 중요한 부분을 뜻하며, 이전 프로젝트에서 진행한 로또 게임에서의 도메인 로직은 아래와 같습니다.

 

- 수익률에 대한 계산이 정확한가?

- 로또 번호에 보너스 넘버를 포함하는지 확인하는 로직이 정확한가?

- 매치 카운터에 대한 게임 result 반환이 정확한가?

 

작성할 수 있는 도메인 로직은 수 없이 많지만, 핵심적인 로직을 위 3가지로 뽑게 되었습니다.

 

class AccuracyTest {
    private val lottoManager = LottoManager()

    @Test
    fun `수익률 계산 정확성 테스트`() {
        val purchasePrice = 5000
        val totalProceeds = 5000
        val managerResult = lottoManager
            .calculateResult(purchasePrice, totalProceeds)
        val trueResult = "100.0"
        assertThat(managerResult).isEqualTo(trueResult)
    }

    @Test
    fun `lotto 번호에 보너스 넘버 포함 테스트`() {
        val gameResult = GameResult.THIRD
        val winningLotto = WinningLotto(setOf(1, 2, 3, 4, 5, 6), 7)
        val lotto = Lotto(listOf(1, 2, 3, 4, 5, 7))
        val managerResult = lottoManager
            .checkBonusResult(gameResult, winningLotto, lotto)
        assertThat(managerResult).isEqualTo(GameResult.SECOND)
    }

    @Test
    fun `lotto 번호에 보너스 넘버 비포함 테스트`() {
        val gameResult = GameResult.THIRD
        val winningLotto = WinningLotto(setOf(1, 2, 3, 4, 5, 6), 7)
        val lotto = Lotto(listOf(1, 2, 3, 4, 5, 8))
        val managerResult = lottoManager
            .checkBonusResult(gameResult, winningLotto, lotto)
        assertThat(managerResult).isEqualTo(GameResult.THIRD)
    }

    @Test
    fun `lotto 번호에 보너스 넘버는 포함되지만 matchNumber이 5가 아닌 경우 테스트`() {
        val gameResult = GameResult.FOURTH
        val winningLotto = WinningLotto(setOf(1, 2, 3, 4, 5, 6), 7)
        val lotto = Lotto(listOf(1, 2, 3, 4, 8, 7))
        val managerResult = lottoManager
            .checkBonusResult(gameResult, winningLotto, lotto)
        assertThat(managerResult).isEqualTo(GameResult.FOURTH)
    }

    @Test
    fun `matchCount에 따른 올바른 GameResult 반환 테스트`() {
        val matchCount = 4
        val managerResult = lottoManager.getMathResult(matchCount)
        assertThat(managerResult).isEqualTo(GameResult.FOURTH)
    }
}

 

 

도메인 로직 분리의 필요성

도메인 로직을 분리하면 관심사를 분리하는 장점을 얻을 수 있습니다.

도메인 로직과 다른 유형 로직을 구별할 수 있으며, 도메인 로직을 명시적으로 구분하고 차별화하는 방법으로 관심사를 분리합니다.

 

위와 같은 명확한 분리를 통해서 UI 문제 같은 다른 세부 사항에 신경 쓰지 않고, 오직 기능적인 로직에만 집중할 수 있게 됩니다.