[SB_BASIC] 단위테스트와 통합테스트

junghan·2023년 5월 26일
0

SpringBootProject

목록 보기
15/35
post-thumbnail

DB와 연동하고 스프링 빈을 주입하는 통합테스트를 해야될 때가 있습니다.
일반적으로는 단위테스트로 모든 기능을 분할하여 최소한으로 진행하는 것이 속도면이나 독립성면에서 유리하다고 생각하지만, 분명히 통합테스트가 필요한 상황이 있습니다.


실제 운영 환경에서 사용될 클래스들을 통합하여 테스트해주는 @SpringBootTest 어노테이션에 대해 알아보도록 하겠습니다.

1. @SpringBootTest

@SpringBootTest는 스프링 컨테이너와 DB까지 연결하여 통합 테스트를 할 수 있도록 도와주는 어노테이션입니다.

실제 운영 환경에서 스프링 컨테이너와 테스트를 함께 실행할 수 있습니다.

2. @Transactional

테스트 케이스에 이 애노테이션이 있으면, 테스트 시작 전에 트랜잭션을 시작하고, 테스트 완료 후에 항상 롤백합니다. 이렇게 하면 DB에 데이터가 남지 않으므로 다음 테스트에 영향을 주지 않게 됩니다.

단위 테스트에서는 @BeforeEach, @AfterEach 어노테이션을 사용해서 데이터를 설정하고 비워주는 작업을 했었는데, 이를 대신한다고 생각하면 좋습니다.

3. 단위 테스트와 통합 테스트이 비교 예시

단위 테스트

package com.springboot.basic.service;

import com.springboot.basic.domain.Member;
import com.springboot.basic.repository.MemoryMemberRepository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

class MemberServiceTest {

    MemberService memberService;
    MemoryMemberRepository memberRepository;

    @BeforeEach
    public void beforeEach() {
        memberRepository = new MemoryMemberRepository();
        memberService = new MemberService(memberRepository);
    }

    @AfterEach
    public void afterEach() {
        memberRepository.clearStore();
    }

    @Test
    void joinSuccess() {
        //given
        Member member = new Member();
        member.setName("hello");

        //when
        Long saveId = memberService.join(member);

        //then
        Member findMember = memberService.findOne(saveId).get();
        assertThat(member.getName()).isEqualTo(findMember.getName());
    }

    void joinFail() {
        //given
        Member member1 = new Member();
        member1.setName("spring");


        Member member2 = new Member();
        member2.setName("spring");

        //when
        memberService.join(member1);
        IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));

        assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원임");

//        try {
//            memberService.join(member2);
//            fail();
//        } catch (IllegalStateException e) {
//            assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원임");
//        }
        //then
    }

}

통합 테스트

package com.springboot.basic.service;

import com.springboot.basic.domain.Member;
import com.springboot.basic.repository.MemberRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

@SpringBootTest
@Transactional
class MemberServiceIntegrationTest {

    @Autowired MemberService memberService;
    @Autowired MemberRepository memberRepository;

    @Test
    public void 회원가입() throws Exception {

        //Given
        Member member = new Member();
        member.setName("WAE3");

        //When
        Long saveId = memberService.join(member);

        //Then
        Member findMember = memberRepository.findById(saveId).get();
        assertEquals(member.getName(), findMember.getName());

    }

    @Test
    public void 중복_회원_예외() throws Exception {

        //Given
        Member member1 = new Member();
        member1.setName("spring");

        Member member2 = new Member();
        member2.setName("spring");

        //When
        memberService.join(member1);
        IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));//예외가 발생해야 한다.

        assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원임");

    }

}

결론

일반적으로는 단위테스트로 커버리지를 일정부분 이상 확보한 뒤, 나중에 통합테스트를 통해 전체적인 흐름을 검증하는 방식으로 하는 것으로 알고 있습니다.

왜냐하면 통합 테스트는 스프링 환경 구성을 위하여 어플리케이션의 설정이나 Bean을 로드하는 시간이 오래걸리기 때문에 검증 시간이 오래걸리고, 테스트 단위가 커서 디버깅이 비교적 어렵기 때문입니다.

다음 프로젝트를 할 때, 두가지 방법을 사용하여 테스트 해볼 생각입니다.

profile
42seoul, blockchain, web 3.0

0개의 댓글