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

Ch01. 도서관리 애플리케이션 리팩토링 준비하기 - 수동 테스트 코드 작성하기

webmaster 2022. 10. 29. 16:57
728x90

사칙연산 계산기에 대한 테스트 코드 작성(순수한 kotlin 코드)

사칙 연산 코드(Calculator.kt)

class Calculator(
    //private var _number: Int //생성자로 받아 이 값을 계속 업데이트 할 예정
    var number: Int
) {
    /*
    val number: Int
        get() = this.number
     */
    fun add(operand: Int) {
        this.number += operand
    }

    fun minus(operand: Int) {
        this.number -= operand
    }

    fun multiply(operand: Int) {
        this.number *= operand
    }

    fun divide(operand: Int) {
        if (operand == 0)
            throw IllegalArgumentException("0으로 나눌 수 없습니다.ㄴ")
        this.number /= operand
    }
}

Test 코드

class CalculatorTest {
    /**
     * 덧셈 테스트
     */
    fun addTest() {
        //given : 테스트 준비
        val calculator = Calculator(5)

        //when : 테스트 기능 호출
        calculator.add(3)
        //비교 방법1: data클래스 사용
        /*
        val expectedCalculator = Calculator(8)
        if (calculator != expectedCalculator) {
            throw IllegalStateException()
        }
         */
        //비교 방법2: private 키워드를 제거
        //단 이방법은 number 값을 setter를 통해 변경할 수 있기 때문에

        //then : 테스트 결
        if(calculator.number != 8){
            throw IllegalStateException()
        }
    }


    fun minusTest() {
        //given
        val calculator = Calculator(5)

        //when
        calculator.minus(3)

        //then
        if(calculator.number != 2){
            throw IllegalStateException()
        }
    }

    fun multiplyTest() {
        //given
        val calculator = Calculator(5)

        //when
        calculator.multiply(3)

        //then
        if(calculator.number != 15){
            throw IllegalStateException()
        }
    }
    
    fun divideTest() {
        //given
        val calculator = Calculator(5)

        //when
        calculator.divide(2)

        //then
        if (calculator.number != 2) {
            throw IllegalStateException()
        }
    }

    fun divideExceptionTest() {
        //given
        val calculator = Calculator(5)

        //when : IllegalArgumentException이 발생하기를 기대한다
        try {
            calculator.divide(0)
        }catch (e: IllegalArgumentException){
            if(e.message != "0으로 나눌 수 없습니다.")
                throw IllegalStateException("메시지가 다릅니다.")
            //테스트 성공
            return
        }catch (e: Exception){
            throw IllegalStateException()
        }
        throw IllegalStateException("기대하는 예외가 발생하지 않았습니다.")
    }

}

fun main() {
    val calculatorTest = CalculatorTest()
    calculatorTest.addTest()
    calculatorTest.minusTest()
    calculatorTest.multiplyTest()
    calculatorTest.divideTest()
    calculatorTest.divideExceptionTest()
}
  • main에서 작성한 테스트 코드를 실행한다.
    • 덧셈, 뺄셈, 곱셈, 나눗셈
  • 테스트를 하기 위해서는 Calculator을 number를 가지고 와야 하는 데, 방법은 2가지가 있다.
    • data 클래스로 만들어 equals 를 실행시키자 (if(calculator!= expectedResult)를 실행하면 내부적으로 equals 호출
    • Calculator에서, number를 가지고 올 수 있는데 private 키워드를 제거하거나(값을 막 변경할 수 있는 단점이 있다), 원래 존재하던 number 프로퍼티를 _number 이름으로 변경하고, 불변 number를 추가하는 방법도 있다.(백킹 프로퍼티)
  • 테스트 메소드는 크게 3단계로 구성된다(given, when, then)
    • given : 테스트 대상을 만들어 준비하는 과정
    • when: 실제 테스트하고 싶은 기능을 호출하는 과정
    • then : 호출 이후 의도한 대로 결과가 나왔는지 확인하는 과정
  • 나눗셈 같은 경우, 0으로 나누었을때 예외가 발생하는지와, 0으로 나누지 않았을 때 정상적으로 동작하는지를 테스트해야 한다.
    • 정상적으로 동작했을 때 (divideTest)는 다른 테스트 코드와 같다.
    • 정상적으로 동작하지 않고, 예외가 발생했는지를 테스트할 때는 try-catch 구문을 통해, 발생되는 예외를 잡고, 해당 부분에서 메시지 또한 같은지를 확인할 수 있다.(IllegalArgumentException이 발생하지 않을 경우 테스트가 정상적으로 동작하지 않음으로 다른 예외를 발생시킨다.)

수동으로 만든 테스트 단점

  • 테스트 클래스가 많아지고, 메서드가 생길 때마다 메인 메서드에 수동으로 코드를 작성해 주어야 한다
    • 메인 메서드가 커지고, 개별적 실행이 어렵다.
  • 테스트가 실패할 경우 무엇을 기대하고, 어떤 값이 들어와 실패했는지 알려주지 않는다. 또한 예외를 던지거나, try-catch를 사용해야 하는 등 직접 구현해야 할 부분이 많다.
  • 테스트 메서드 별로 공통적으로 처리해야 하는 기능이 있다면, 메서드 중복이 생기게 된다

이러한 단점을 극복하기 위해 테스트 프레임워크가 등장하였다(JUnit5)

 

728x90