본문 바로가기
부트캠프 개발일기/Spring MVC

54일차: 테스팅(단위테스트, JUnit)

by shyun00 2023. 4. 28.

❯ 테스팅(Testing)

애플리케이션 구현을 하고나면 코드가 의도한대로 작동하는지 테스트를 해야한다.

테스트란 어떤 대상에 대한 기준을 정해놓고 정해진 기준에 맞는지 검증하는것을 의미한다.

 

애플리케이션 테스트는 크게 네가지로 구분할 수 있다.

  • 기능테스트: 애플리케이션을 사용하는 사용자 입장에서 기능이 올바르게 동작하는지 테스트 하는것
  • 통합테스트: 클라이언트 툴 없이 테스트 코드를 통해 애플리케이션이 정상 동작하는지 테스트 하는것
  • 슬라이스 테스트: 특정 계층이 올바르게 동작하는지 테스트 하는것. Mock(가짜) 객체를 사용해서 테스팅
  • 단위테스트: 기능이 제대로 동작하는지 테스트하는것. 대부분 메서드 단위로 작성됨.

단위테스트 F.I.R.S.T 원칙

Fast: 테스트 케이스는 빨라야한다.

Independent: 각각의 테스트 케이스는 독립적이어야한다.(다른 테스트에 영향을 주지 않아야한다.)

Repeatable: 어떤 환경에서든 반복수행이 가능해야한다.

Self-validating: 성공 혹은 실패라는 검증 결과를 보여줄 수 있어야한다.

Timely: 테스트하려는 기능을 구현하기 직전에 작성해야한다. TDD(테스트 주도 개발)

 

단위테스트를 위한 테스트 코드를 작성할때 Given-When-Then 형태를 따르면 구조가 명확해진다.

Given: 테스트를 위한 준비 내용 명시(테스트 입력값, 전제조건 등)

When: 테스트할 동작을 지정한다. 대상 메서드를 호출하는 부분이다.

Then: 테스트 결과를 검증(Assertion)하는 부분이다.

❯ JUnit

 JUnit이란 자바 언어로 만들어진 애플리케이션을 테스트하기 위한 오픈소스 테스트 프레임워크를 말한다.

Spring Boot를 통해 프로젝트를 생성하면 기본적으로 org.springframework.boot:spring-boot-startet-test 스타터가 포함되어

별도 설정 없이 사용이 가능하다. (src/test 디렉토리가 자동으로 생성됨)

@Test, @DisplayName("...")

JUnit을 사용한 테스트 케이스에는 @Test애너테이션을 붙여서 테스트 코드임을 명시한다.

@DisplayName("...") 애너테이션을 통해 해당 테스트 케이스의 이름을 지정해줄 수 있다.

public class HelloJUnitTest {
    @DisplayName("Hello JUnit Test")
    @Test
    public void assertionTest(){
        String expected = "Hello, JUnit";
        String actual = "Hello, JUnit";

        assertEquals(expected, actual);
    }
}

위 테스트 코드를 실행시키면 아래와 같이 "Hello JUnit Test" 테스트가 정상적으로 수행된 것을 알 수 있다.

Assertion

Assertion은 자바 테스팅에서 예상하는 결과가 참(true)이기를 바라는 논리적인 표현을 의미한다.

Assertions 클래스 설명

Assertions 클래스의 static 메서드를 통해 결과를 검증할 수 있다.

assertEquals(expected, actual) 을 통해 두 파라미터가 일치하는지 검증할 수 있으며

assertNotEquals(unexpected, actual) 을 통해 두 파라미터가 일치하지 않는지도 검증할 수 있다.

다양한 static 메서드가 제공된다.

테스트 전처리와 후처리

테스트를 실행하기 전에 어떤 객체나 값을 초기화해야하는 경우가 있다.

이때 사용할 수 있는 애너테이션으로 @BeforeEach, @BeforeAll이 있다.

  • @BeforeEach: 테스트 클래스 내의 테스트가 실행될 때, 개별 테스트 실행 직전에 먼저 실행됨. (여러번 반복 가능)
  • @BeforeAll: 테스트 클래스 레벨에서 테스트를 실행시키면 딱 한번만 초기화된다.(한번만 수행됨)
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class BeforeEach1Test {
    @BeforeAll
    public static void beforeAll(){
        System.out.println("@BeforeAll 수행");
    }

    @BeforeEach
    public void init(){
        System.out.println("@BeforeEach 수행");
    }

    @DisplayName("@BeforeEach Test1")
    @Test
    public void beforeEachTest1(){
        System.out.println("Test1 수행");
    }

    @DisplayName("@BeforeEach Test2")
    @Test
    public void beforeEachTest2(){
        System.out.println("Test2 수행");
    }
}

BeforeAll은 초기에 한번만 수행되며 BeforeEach는 매번 테스트마다 수행된 것을 알 수 있다.

반대로, 테스트가 종료되고나서 후처리를 담당하는 애너테이션으로는 @AfterEach, @AfterAll 이 있다.

Assumption을 활용한 조건부 테스트

특정 조건을 만족할때만 테스트를 수행하도록 지정해야하는 경우가 있다.

이 때 사용가능한 메서드가 assumeTrue(조건내용)이다. 조건내용이 True일때만 assumeTrue() 다음 로직이 수행되며,

False 일때는 로직이 실행되지 않는다.

❯ Hamcrest

JUnit 기반 단위테스트에서 사용할 수 있는  Assertion Framework이다.

사용 방법은 Assertions 클래스 메서드와 유사하나 표현방법과 출력결과가 자연스러운 문장으로 이어지므로

가독성이 향상된다는 장점이 있다.

public class HelloHamcrestTest {

    @DisplayName("Hello Junit Test using hamcrest")
    @Test
    public void assertionTest() {
        String expected = "Hello, World";
        String actual = "Hello, JUnit";

        assertThat(actual, is(equalTo(expected))); // 문장과 같은 형태의 코드
    }
}