본문 바로가기
개발/Spring

[Spring] 단위 테스트를 위한 JUnit

by tempus 2021. 8. 26.
반응형

개발에서 테스트는 매우 중요한 부분입니다. 특히 비즈니스 로직을 중점적으로 개발하는 백엔드 개발자에게 있어서 테스트는 언제나 고민이 되는 부분일 것입니다. 그래서 요즈음 TDD(Test-driven Development) , 테스트 주도 개발이라는 소프트웨어 방법론을 많이 사용하고 있습니다. 간략히 설명하면 단위 테스트 케이스를 작성하고 이를 통과하는 개발 코드를 작성하는 방법입니다.


Spring에서 단위 테스트를 위해 주로 사용하는 프레임워크인 JUnit에 대해 정리해보려고 합니다. 이 글에서는 어노테이션(annotation)Assert을 중점으로 정리를 할 것입니다.

junit_logo

 

해당 글에서는 테스트를 위해 Junit5를 사용할 것입니다. Junit5는 이전 버전과 다르게 3개의 서브 프로젝트로 이루어져 있습니다. (JUnit5는 java 8부터 지원합니다.)

JUnit Platform + JUnit Jupiter + JUnit Vintage로 이루어져 있습니다.

  • JUnit Platform : JVM에서 테스트 프레임워크를 실행하는데 기초를 제공
  • JUnit Jupiter : JUnit5에서 테스트를 작성하고 확장을 하기 위한 새로운 프로그래밍 모델과 확장 모델의 조합
  • JUnit Vintage : 하위 호환성을 위해 JUnit3와 JUnit4를 기반으로 돌아가는 플랫폼에 테스트 엔진 제공

 

기본적인 테스트를 작성하기 위한 샘플은 다음과 같습니다.

import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;

public class ExampleTest {

    private  final SimpleModel simpleModel = new SimpleModel();

    @Test
    public void Test(){
       assertEquals(2, simpleModel.add(1,1));
    }

}

해당 테스트가 정상적으로 이루어지면 다음과 같은 결과가 나옵니다.


test_success
테스트 성공

테스트가 실패 시에는 아래와 같이 failed라고 알려줍니다.


test_fail
테스트 실패

그리고 테스트 코드 가독성을 높이기 위해 아래와 같은 템플릿에 따라 작성하기도 합니다. (By 스프링 강의)

@Test
public void findByName() {
    //given -> 테스트 실행을 위한 조건 설정
    Member member1 = new Member();
    member1.setName("spring1");
    repository.save(member1);
    Member member2 = new Member();
    member2.setName("spring2");
    repository.save(member2);

    //when -> 실제로 테스트 하기 위한 코드 작성
    Member result = repository.findByName("spring1").get();

    //then -> 테스트 결과
    assertThat(result).isEqualTo(member1);
}

JUnit Annotation

이제 본격적으로 테스트 코드를 처음 작성해보는 개발자를 대상으로 JUnit에서 자주 사용하는 어노테이션에 대해 알아보자

  • @DisplayName : 테스트 클래스나 테스크 메소드에 이름을 붙여줄 때 사용
@Test
@DisplayName("Testing for add function")
public void Test(){
    assertEquals(2, simpleModel.add(1,1));
}

 

test_method_setting

  • @BeforeEach : 테스트 메소드가 실행되기전에 실행되어야 하는 메소드를 명시해줍니다. 일반적으로 테스트 전에 필요한 데이터를 세팅해주기 위해 주로 사용합니다.
@BeforeEach
public void beforeEach(){

    memberRepository = new MemoryMemberRepository();
    memberService = new MemberService(memberRepository);
}
  • @AfterEach : @Test가 붙은 테스트 메소드가 실행되고 난 후 실행됩니다. 일반적으로 테스트 실행 후 초기화를 위해 사용합니다.
@AfterEach
public void afterEach(){
    repository.clearStore();
}
  • @AfterAll : @AfterEach와 다르게 테스트가 완전히 끝난 후 딱 한 번만 실행합니다.
@AfterAll
public  static void afterAll(){
    //do Something
}
  • @BeforeAll : @BeforeEach와 다르게 테스트가 시작히기 전 딱 한번만 실행합니다.
@BeforeAll
public  static void beforeAll(){
    //do Something
}
  • @Disabled : 테스트 클래스나 메소드의 테스트를 비활성화 합니다.
@Test
@Disabled
public void disabled_test() {
    //do something
}

위에 어노테이션으로 테스트를 실행했을 때 실행되는 순서는 다음과 같습니다.

 

test_sequence
어노테이션 실행 순서 

JUnit Assert

어노테이션과 더불어 중요한 JUnit Assert에 대해서 알아봅시다. assert는 테스트에 넣을 수 있는 정적 메서드 호출입니다. 각 Assert 구문은 어떤 조건이 참인지 검증하는 방법입니다. 설정한 조건이 참이 아니면 테스트는 실패합니다.


  • assertEquals() : 오른쪽의 결과가 왼쪽과 동일한지 확인하는 구문
@Test
public void Test(){
    assertEquals(2, simpleModel.add(1,1));
}
  • assertTrue() : 해당 함수의 return 값이 true이면 성공
@Test
public void Test(){
    assertTrue(true); //true에 특정 함수 삽입
}
  • assertTimeout() : 특정 시간동안 테스트가 끝나지 않으면 테스트를 실패시키는 구문
@Test
public void Test(){
    assertTimeout(Duration.ofMinutes(1), () -> {
        //1분 미만으로 동작되는 함수 실행
    });
}
  • assertTimeoutPreemptively() : 특정 시간동안 테스트가 끝나지 않으면 테스트를 실패시키는 구문 하지만 위와 다르게 특정 시간이 지나면 테스트를 종료
@Test
public void Test(){
    assertTimeoutPreemptively(Duration.ofMinutes(20), () -> {
        // 20분 이상 걸리는 작업
    });
}

하지만 JUnit Jupiter가 제공해주는 assertion은 많은 테스트 시나리오에는 부족할 수 있기 때문에 JUnit에서 AssertJ ,Truth, Hamcrest와 같은 써드 파티 라이브러리를 쓰는 걸 추천합니다.

반응형

댓글


loading