테스팅

박윤택·2022년 10월 9일
0

Spring

목록 보기
12/18

테스트

기능 테스트

  • "애플리케이션을 사용하는 사용자 입장에서 애플리케이션이 제공하는 기능이 올바르게 동작하는지를 테스트"
  • 개발자 이외의 제 3자가 테스트의 주체가 된다.

통합 테스트

  • "API를 호출하는 테스트 코드를 통해 서비스 계층과 데이터 엑세스 계층을 거쳐 DB에 실제로 접속해서 기대했던 대로 동작하는지 테스트 하는 것"
  • 주로 개발자가 짜놓은 테스트 코드를 실행
  • 개발자 또는 개발팀이 테스트의 주체

슬라이스 테스트

  • "애플리케이션을 특정 계층으로 쪼개어서 하는 테스트"
  • 부분 통합 테스트라고 부르기도 한다.

단위 테스트

  • "메서드 단위로 테스트 하는 것"

F.I.R.S.T 원칙

  • Fast(빠르게)
    작성한 테스트 케이스는 빨라야 한다는 의미
  • Independent(독립적으로)
    각각의 테스트 케이스는 독립적이어야 한다는 의미
  • Repeatable(반복 가능하도록)
    테스트 케이스는 어떤 환경에서도 반복해서 실행이 가능해야 된다는 의미
  • Self-validating(셀프 검증이 되도록)
    단위 테스트는 성공 또는 실패라는 자체 검증 결과를 보여주어야 한다는 의미
  • Timely(시기 적절하게)
    단위 테스트는 테스트 하려는 기능 구현을 하기 직전에 작성해야 한다는 의미

Given-When-Then 표현 스타일

  • Given
    테스트를 위한 준비 과정을 명시한다.
  • When
    테스트 할 동작(대상)을 지정한다.
  • Then
    테스트의 결과를 검증(Assertion)한다.

JUnit 적용

JUnit 테스트 케이스 기본 구조

import org.junit.jupiter.api.Test;

public class JunitDefaultStructure {
    @Test
    public void test1() {
        // 테스트 하고자 하는 대상에 대한 테스트 로직 작성
    }

    @Test
    public void test2() {
        // 테스트 하고자 하는 대상에 대한 테스트 로직 작성
    }

    @Test
    public void test3() {
        // 테스트 하고자 하는 대상에 대한 테스트 로직 작성
    }
}

AssertJ 사용


AssertJ 문서 - 적용

  • AssertJ methods static import
import static org.assertj.core.api.Assertions.*;

예시

// entry point for all assertThat methods and utility methods (e.g. entry)
import static org.assertj.core.api.Assertions.*;

// basic assertions
assertThat(frodo.getName()).isEqualTo("Frodo");
assertThat(frodo).isNotEqualTo(sauron);

// chaining string specific assertions
assertThat(frodo.getName()).startsWith("Fro")
                           .endsWith("do")
                           .isEqualToIgnoringCase("frodo");

// collection specific assertions (there are plenty more)
// in the examples below fellowshipOfTheRing is a List<TolkienCharacter>
assertThat(fellowshipOfTheRing).hasSize(9)
                               .contains(frodo, sam)
                               .doesNotContain(sauron);

// as() is used to describe the test and will be shown before the error message
assertThat(frodo.getAge()).as("check %s's age", frodo.getName()).isEqualTo(33);

// exception assertion, standard style ...
assertThatThrownBy(() -> { throw new Exception("boom!"); }).hasMessage("boom!");
// ... or BDD style
Throwable thrown = catchThrowable(() -> { throw new Exception("boom!"); });
assertThat(thrown).hasMessageContaining("boom");

// using the 'extracting' feature to check fellowshipOfTheRing character's names
assertThat(fellowshipOfTheRing).extracting(TolkienCharacter::getName)
                               .doesNotContain("Sauron", "Elrond");

// extracting multiple values at once grouped in tuples
assertThat(fellowshipOfTheRing).extracting("name", "age", "race.name")
                               .contains(tuple("Boromir", 37, "Man"),
                                         tuple("Sam", 38, "Hobbit"),
                                         tuple("Legolas", 1000, "Elf"));

// filtering a collection before asserting
assertThat(fellowshipOfTheRing).filteredOn(character -> character.getName().contains("o"))
                               .containsOnly(aragorn, frodo, legolas, boromir);

// combining filtering and extraction (yes we can)
assertThat(fellowshipOfTheRing).filteredOn(character -> character.getName().contains("o"))
                               .containsOnly(aragorn, frodo, legolas, boromir)
                               .extracting(character -> character.getRace().getName())
                               .contains("Hobbit", "Elf", "Man");

슬라이스 테스트

API 계층 테스트

  • @SpringBootTest를 이용한 controller 테스트 클래스 기본 구조
@SpringBootTest      
@AutoConfigureMockMvc  
public class ControllerTestDefaultStructure {
    @Autowired
    private MockMvc mockMvc;
    

    @Test
    public void postMemberTest() {
        // given 
        
        // when 
        
        // then 
    }
}

MockMvc의 기능을 사용하기 위해 @AutoConfigureMockMvc를 추가해야 한다.
MockMvc는 서버를 실행시키지 않고 Spring 기반 애플리케이션을 테스트 할 수 있는 환경을 지원해주는 테스트 프레임워크이다.


  • @WebMvcTest를 이용한 controller 테스트 클래스 기본 구조
@ExtendWith(SpringExtension.class)      
@WebMvcTest(Controller.class)
public class ControllerTestDefaultStructure {
    @Autowired
    private MockMvc mockMvc;
    

    @Test
    public void postMemberTest() {
        // given 
        
        // when 
        
        // then 
    }
}

@WebMvcTest는 MVC 부분 슬라이스 테스트로, 보통 컨트롤러 하나만 테스트하고 싶을 때 사용한다. @SpringBootTest에는 다음과 같이 @ExtendWith(SpringExtension.class)가 포함되어 있다.


  • @SpringBootTest + @AutoConfigureMockMvc VS @WebMvcTest
    @SpringBootTest는 통합 테스트에 해당된다. 기본적으로 @SpringBootTest 어노테이션을 이용하면 스프링이 관리하는 모든 빈을 등록시켜 테스트하기 때문에 무겁다. 하지만 @WebMvcTest는 웹과 관련된 빈들만 등록하므로 비교적 가볍다. 웹 레이어 관련 빈들만 등록되므로 Service는 등록되지 않는다.(즉, @Component는 스캔 대상에서 제외) 따라서 @MockBean 어노테이션을 이용해 가짜 객체를 만들어줘야한다.

데이터 엑세스 계층 테스트

데이터 엑세스 계층 테스트 시에는 테스트 종료 후, DB의 상태를 테스트 케이스 실행 이전으로 되돌려서 깨끗하게 만들어야한다.

@DataJpaTest
public class MemberRepositoryTest {
    ...
	...

    @Test
    public void findByEmailTest() {
        // given 
        Member member = new Member();
        member.setEmail("hgd@gmail.com");
        member.setName("홍길동");
        member.setPhone("010-1111-2222");

        // when 
        memberRepository.save(member); 
        Optional<Member> findMember = memberRepository.findByEmail(member.getEmail()); 

		// then 
        assertTrue(findMember.isPresent()); 
        assertTrue(findMember.get().getEmail().equals(member.getEmail())); 
    }
}

@DataJpaTest 어노테이션을 이용하여 하나의 테스트 케이스 실행이 종료되는 시점에 데이터베이스에 저장된 데이터는 rollback처리한다.

rollback이 되는 이유는 @DataJpaTest 어노테이션 내부에 @Transactional 어노테이션이 존재하기 때문이다.


0개의 댓글