Spring Boot & 테스트 코드 작성 (1) - JUnit

Spring Boot & 테스트 코드 작성 (1) - JUnit

테스트 코드

이제는 어딜가나 빼놓을 수 없는게 바로 테스트 코드이다.

이번엔 Spring Boot 와 함께 Gradle 환경에서 테스트 코드를 작성하고 결과를 확인해보도록 하자.

단위 테스트(Unit Test)

단위 테스트에 대한 개념은 대부분의 블로그에서 다루고 있기에 여기서는 단순한 개념만 얘기하도록 하겠다.

그래서 유닛테스트(Unit Test)가 뭔가요?

Unit Test (단위 테스트) 에 관한 생각

Unit Test (단위 테스트) 도입하기 - 1편

단위 테스트 활용 방법: JUnit 참조 가이드

단위 테스트는 프로그램내의 함수나 객체들을 검사하여 코드 변경으로부터 자유롭게 개발 할 수 있도록 하는 절차이다.

단위 테스트의 각 테스트는 모두 독립적이여야 하며 하나의 테스트는 하나의 시나리오만을 검사한다.

단위 테스트는 버그를 찾기 위한 것이 아닌 개별 단위의 적합성 혹은 정확성을 확인하기 위한 방법이다.

JUnit

단위 테스트 Framework 중 하나로 JAVA 의 단위 테스트 프레임워크 표준이다.

JUnit 은 xUnit 계열의 단위 테스팅 프레임워크로 각 언어별 다음과 같은 xUnit 들이 있다.

xUnit Name Lang 관련 사이트
CUnit C http://cunit.sourceforge.net/
CppUnit C++ https://sourceforge.net/projects/cppunit/
PHPUnit PHP https://phpunit.de/
PyUnit Python http://pyunit.sourceforge.net/
JUnit Java http://junit.org/

많이쓰이는 버전은 JUnit4 와 5 버전이다.

스프링부트 프레임워크에 포함되어 있을 정도로 많이 쓰이고 있다.

단정문으로 테스트 케이스의 수행 결과를 판별한다.

ex) assertEquals(예상 값, 실제 값)

어노테이션으로 간결하게 적용할 수 있다.

결과는 성공(녹색), 실패(붉은색) 중 하나로 표시된다.

테스트 러너

[JUnit] 테스트 러너(test runner)란?

테스트를 실행하여 그 결과를 보여주는 것이 테스트 러너이다.

JUnit 사용

JUnit assert관련 Method 는 아래와 같이 있다.

Method 내용
assertArrayEquals(a,b) 배열 a와b가 일치함을 확인한다.
assertEquals(a,b) 객체 a와b의 값이 같은지 확인한다.
assertSame(a,b) 객체 a와b가 같은 객체임을 확인한다.
assertTrue(a) a가 참인지 확인한다.
assertFalse(a) a가 거짓인기 확인한다.
assertNotNull(a) a객체가 Null이 아님을 확인한다.

JUnit 설치

앞선 Spring Boot 예제를 이용하여 Test 를 진행한다.
Spring Boot 예제는 [이곳] 에서 볼 수 있다.

JUnit 설치를 위해서 의존성(dependency)를 추가해 주어야 하는데 Spring Boot Framework에 포함되어있어 다음과 같이 불러올 수 있다.

JUnit 의존성

중요한 점은 Spring Boot 버전에 따라 JUnit 버전이 달라지는데 Gradle 에서 버그가 있다고 한다.
혹시 ClassNotFoundException: org.junit.platform.engine.EngineDiscoveryListener 에러가 발생한다면 아래 블로그를 참고 바란다.

https://yeti.tistory.com/237

결론은 JUnit platform launcher 버전 문제인데 Spring boot 프레임워크는 최신버전이지만 Launcher 버전이 구버전이여서 다음과 같이 최신버전 받도록 의존성을 추가해주면 해결된다. (1.6 이상 버전을 직접 추가해 주어도 된다)

...
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testImplementation 'org.junit.platform:junit-platform-launcher'
}
...

이후 어노테이션을 이용하여 JUnit 의 기능들을 사용하면 된다.

테스트 실행은 VSCode 의 경우 Test 탭에서 실행할 수 있으며
CodeLens 기능을 이용해 소스에서 직접 실행도 가능하다.

Test 탭은 IDE 좌측 시약 아이콘을 누르면 나타나고

VSCode JUnit

CodeLens는 소스코드에 바로 나와있어 클릭만 하면된다.

VSCode CodeLens

이렇게 테스트를 실행하면 하단에 DEBUG CONSOLE 을 통해 테스트 실행 내역을 확인할 수 있다.

VSCode DebugConsole

Test 예제

실제 JUnit 을 사용해보자

JUnit 을 테스트 하기위한 제품 할인율 계산용 class를 만들 것이다.
제품 객체는 가격과 할인율을 가지고 원가와 할인가격을 알려준다.

// (Product.java)
package com.example3.demo.entity;

public class Product {

    int prodPrice;
    int prodSaleRate;

    public int getProdPrice() {
        return this.prodPrice;
    }

    public void setProdPrice(int prodPrice) {
        this.prodPrice = prodPrice;
    }

    public int getProdSaleRate() {
        return this.prodSaleRate;
    }

    public void setProdSaleRate(int prodSaleRate) {
        if (prodSaleRate > 100) prodSaleRate = 100;
        if (prodSaleRate < 0) prodSaleRate = 0;
        this.prodSaleRate = prodSaleRate;
    }

    public int getProdSalePrice() {
        return (int)(this.prodPrice - this.prodPrice * (0.01 * this.prodSaleRate));
    }
}

이제 할인율을 테스트 하기위한 테스트 객체를 생성한다.
우리가 사용하는 JUnit 은 5버전이기때문에 @BeforeEach를 사용했다.
(JUnit4 에서는 @Before 사용)

// (ProductTest.java)
package com.example3.demo.entity;

import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class ProductTest {

    Product prod = new Product();
    int price = 4500;
    int saleRate = 30;

    @BeforeEach
    public void setup() {
        prod.setProdPrice(price);
        prod.setProdSaleRate(saleRate);
    }

    @Test
    @DisplayName("Orininal product price")
    public void originalPrice() throws Exception {
        assertTrue(
            price == prod.getProdPrice(), "This is a original price");

    }

    @Test
    public void failPrice() throws Exception {
        assertTrue(
            price == prod.getProdSalePrice(), "It will be fail");
    }

    @Test
    public void salePrice() throws Exception {
        assertTrue(
            (int) price - price * (0.01 * saleRate)
            == prod.getProdSalePrice(), "This is a sale price");
    }
}

이제 Test를 돌리면 다음과 같은 결과가 나온다.

VSCode Test 결과

총 세개의 테스트가 나오는데 앞서 미리 설정한대로 failPrice, originalPrice, salePrice 가 실행되었고

@DisplayName 어노테이션을 통해 originalPriceOriginal product price로 이름이 변경되었다.

이번엔 실패 내역을 살펴보자

JUnit 에러 메세지

assertX 메소드에 미리 넣어둔 It will be fail 이라는 메세지와 함께 true 가 아닌 false 가 반환되었다고 나온다.

이번엔 assertEquals 메소드를 통해 결과를 다시 확인해보자

다음과같이 Equals 메소드로 변경한 후 CodeLens를 이용해 아래 테스트만 실행하면

    @Test
    public void failPrice() throws Exception {
        assertEquals(
            price,
            prod.getProdSalePrice(), 
            "It will be fail");
    }

다음과 같은 메세지로 바뀌어 나온다.

JUnit equal 에러 메세지

지금까지 기본적인 JUnit 의 사용법과 디버깅 방법에 대해서 설명했다.
조금더 합리적인 테스트 코드 작성하기 위해서 단위 테스트와 TDD 관련 내용을 더 찾아보길 추천한다.

2부에서는 Mock 객체를 이용한 Spring Boot 에서의 테스트 방법에 대해 알아보도록 하자

엮인글

Spring Boot & 테스트 코드 작성 (1) - JUnit
Spring Boot & 테스트 코드 작성 (2) - Mock, Mockito
Spring Boot & 테스트 코드 작성 (3) - MockMvc
Spring Boot & Rest API (1)
Spring Boot & Rest API (2) - JPA, MySQL


참고

https://www.slideshare.net/gyumee/ss-90206560
https://martinfowler.com/bliki/UnitTest.html
https://code.visualstudio.com/docs/java/java-testing

+ Recent posts