카테고리 없음

테스트 코드란

푸라멘 2024. 4. 15. 23:20

테스트 코드란?

작성한 코드가 의도된 대로 작동하는지 검증하는 코드

 

 

테스트의 종류

 

단위 테스트 (Unit Test)

  • 하나의 모듈을 기준으로 독립적으로 진행되는 가장 작은 단위의 테스트 (하나의 기능 또는 메소드)
  • 어떤 기능일 실행되면  어떤 결과가 나올지 정도로 테스트한다.

통합 테스트 (Integration Test)

  • 모듈을 통합하는 과정에서 모듈간의 호환성을 확인하기 위해 수행되는 테스트

 

테스트 코드의 장점

  • 개발 과정 중 예상치 못한 문제를 미리 발견할 수 있다.
    • 클라이언트를 통해 하나하나 기능을 작동하지 않고 테스트할 수 있다.
  • 코드 수정이 필요한 상황에서 유연하고 안정적인 대응을 할 수 있게 해준다.
    • 테스트 코드는 변경에 대한 사이드 이펙트를 줄이는 예방책이 된다.
    • 코드 변경시, 변경 부분으로 인한 영향도를 쉽게 파악 할 수 있다.
  • 문서로서의 역할이 가능하다.
    • 테스트 코드는 개발자가 작성한 메소드가 어떻게 동작하고 어떤 결과 반환을 원하는지 알려준다.

 

 

테스트 코드의 단점

  • Unit Test가 다른 객체들과 메세지를 주고 받아야할 경우 독립전인 테스트이기 때문에 다른 객체 역할을 하는 가짜 객체를 만들어서 정해진 결과르 반환받게 만들어야 한다.

 

 

 

좋은 테스트 코드란?

  • 가독성이 좋은 코드
  • 1개의 테스트 함수에 대해 assert 최소화
  • 1개의 테스트 함수는 1가지 개념만을 테스트
  •  FIRST라는 5가지 규칙1
    1. Fast : 테스트는 빨그ㅔ 동작하여 자주 돌리 수 있어야한다.
    2. Independetn : 각각의 테스트는 독립적이며 서로 의존해서는 안된다.
    3. Repeatable : 어느 환경에서도 반복 가능해야한다.
    4. Self-Validating : 테스트는 성공 또는 실패로 bool 값으로 결과를 내어 자체적으로 검증되어야한다.
    5. Timely : 테스트는 적시에 즉, 테스트하려는 실제 코드를 구현하기 직전에 구현해야한다.

 

 

 

단위 테스트 작성 예시

라이브러리

단위 테스트 작성에는 크게 2가지 라이브러리가 사용된다.

  • Junit5 : Java 단위 테스트를 위한 테스팅 프레임 워크
  • AssertJ : Java 테스트르 돕기 위해 다양한 문법을 지원하는 라이브러리 

 

given/when/then 패턴

단위 테스트는 주로 given-when-then 패턴으로 작성된다.

  • given(준비) : 어떠한 데이터가 주어졌는지
  • when(실행) : 어떠한 함수를 실행하는지
  • then(검증) : 어떠한 결과가 나와야 하는지

※ 윈도우 기준 패키지에서 ctrl + shift + t를누르면 바로 test폴더에 test 파일을 생성 할 수 있다.

 

단위 테스트 예제

class MemberServiceTest {

    MemberService memberService;
    //DB가 아닌 로컬 메모리에 저장
    MemoryMemberRepository memberRepository = new MemoryMemberRepository();

    // 각테스트 실행전마다 실행
    @BeforeEach
    public void beforeEach() {
        memberRepository = new MemoryMemberRepository();
        memberService = new MemberService(memberRepository);
    }

	//각 테스트 실행 후마다 실행
    @AfterEach
    public void afterEach() {
        memberRepository.clearStore();
    }

	//테스트 코드 명시
    @Test
    void join() {
        //given
        Member member = new Member();
        member.setName("spring");
        //when
        Long saveId = memberService.join(member);

        //then
        Member findMember = memberService.findOne(saveId).get();
        //서로 비교해서 일치하면 통과
        Assertions.assertThat(findMember.getId()).isEqualTo(member.getId());
    }

    @Test
    //오류가 발생해야하는 케이스
    public void duplicateMemberTest() {
        //given
        Member member1 = new Member();
        member1.setName("spring");
        
        //when
        Member member2 = new Member();
        member2.setName("spring");
        memberService.join(member1);
        
        //then
        //기대했던 오류메세지가 나오면 테스트 통과
         IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
        Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");

    }
}

 

 

통합 테스트 예제

@SpringBootTest
@Transactional
class MemberServiceIntegrationTest {

    @Autowired
    MemberService memberService;
    @Autowired
    MemberRepository memberRepository;
    @Test
    void join() {
        //given
        Member member = new Member();
        member.setName("test");
        //when
        Long saveId = memberService.join(member);

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

    @Test
    public void duplicateMemberTest() {
        //given
        Member member1 = new Member();
        member1.setName("spring3");
        
        //when
        Member member2 = new Member();
        member2.setName("spring3");
        memberService.join(member1);

        //then
        IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
        Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");

    }
}

 

단위테스트와 구성은 비슷하지만 실제 DB와의 상호작용도 고려하여 TEST

  • @SpringBootTest 를 이용하면 모든 빈이 등록되고, 다른 컴포넌트 들과 연결되어 통합테스트가 된다.
    • 불필요한 경우에는 사용하지 않는 경우가 좋다 -> 스프링 설정을 가져오기 때문에 순수 자바코드 테스트보다 느려짐
  • @Transactional을 이용하면 Test가 transaction 단위로 실행되고 실제 DB에 반영되지않고 rollback 되어 여러번 테스트하여도 동일한 결과를 얻을 수 있게 해준다.

 

 

 

 

참고 문헌