ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 테스트 코드란
    카테고리 없음 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 되어 여러번 테스트하여도 동일한 결과를 얻을 수 있게 해준다.

     

     

     

     

    참고 문헌

Designed by Tistory.