Coding/Back - Spring Framework

Spring Testing JUnit 사용 2/3 #Day13-15

꿀딴지- 2023. 9. 11. 13:59

JUnit 단위테스트

  • JUnit5(Spring framework의 버전을 따름 - boot X)
  • 사실상 Java의 표준 테스트 프레임워크

원활한 JUnit 호환을 위해 intelliJ 설정 변경 : Run testing using : IntelliJ IDEA

  • Spring Boot Intializr를 이용해서 프로젝트를 생성하면 기본적으로 testImplementation >'org.springframework.boot:spring-boot-starter-test' 스타터가 포함되며, JUnit도 포함

 

  1. 데이터 전처리
    • @BeforeEach : 테스트 케이스가 각각 실행될 때마다 테스트 케이스 실행 직전에 먼저 실행되어 초기화 작업진행
    • @BeforeAll (static 메서드 앞): 테스트 케이스가 실행되기 전에 딱 한 번만 초기화
    • @AfterEach, @AfterAll
    • Assumption (가정) 특정환경에서만 테스트가 실행되도록 함
      • assumeTrue(System.getProperty("os.name").startsWith("Windows"));
      • 파라미터가 true면 아래 로직 실행
  2. 결과확인(assert)
    • assertEquals(기대값, 실제값)
    • assetNotNull(테스트 대상, 실패 시 출력문구)
    • assertThrows(발생이 기대되는 예외클래스, 람다표현식 → 테스트대상 메서드) ex. assertThrows(NullPointerException.class, () -> getCryptoCurrency("XRP"));
    • assertDoesNotThrow() : 예외가 발생되지 않기를 기대

 

슬라이스 테스트(계층별)

  • 전체를 다 올리지 않고 목적에 맞는 에너테이션으로 테스트 진행
  • @WebMVCTest @DataJpaTest
  • 목업데이터 세팅을 하게 끔 가능하게 해준 툴 : 모키토

API 계층 테스트 : Controller @WebMvcTest

MockMvc는 Tomcat 같은 서버를 실행하지 않고 Spring 기반 애플리케이션의 Controller를 테스트할 수 있는 완벽한 환경을 지원해 주는 일종의 Spring MVC 테스트 프레임워크

Gson 의존성 추가 : 객체를 JSON 포맷으로 변환해주는 라이브러리

dependencies { ..
	implementation 'com.google.code.gson:gson'}
  1. 컨트롤러 테스트 :

     1) 테스트 클래스 구성

    • @WebMvcTest : Controller에서 의존하는 컴포넌트들을 모두 일일이 설정
    • @SpringBootTest, @AutoConfigureMockMvc : application context를 생성하고, Controller 테스트를 위한 애플리케이션의 자동 구성함

     2) 테스트 메서드 작성

    1. Given : 테스트 요청 데이터
      • Gson으로 json 형태로 변경
    2. When : 테스트 내용
      • mockMvc.perform() : MockMvc로 테스트 대상 Controller의 핸들러 메서드에 요청을 전송 → ResultActions 타입의 객체 리턴
    3. Then : 전송한 데이터 검증 resultActions.andExpect()
      • .andExpect(status().isCreated()) , .andExpect(*status*().isOk())
      • header().string("Location", is(startsWith("/v11/members/")))
@SpringBootTest
@AutoConfigureMockMvc
public class MemberControllerTest {

    @Autowired // 의존성 주입방법 중 하나
    private MockMvc mockMvc;
    @Autowired
    private Gson gson;;

    @DisplayName("CONTROLLER : Member post 테스트")
    @Test
    public void postMemberTest() throws Exception{
        //1. given
        MemberPostDto post = new MemberPostDto("jy@email.com","ㅂㅂㅂ","010-1234-5678");
        String content = gson.toJson(post);

        //2. when
        ResultActions actions =
                mockMvc.perform(
                        post("/rv1/members") //HTTP POST METHOD와 request URL을 설정
                                .accept(MediaType.APPLICATION_JSON)
                                .contentType(MediaType.APPLICATION_JSON)
                                .content(content));

        //3. then
        actions
                .andExpect(status().isCreated());
                //.andExpect(header().string("Location", is(startsWith("/rv1/members/"))));

    }
}
@SpringBootTest
@AutoConfigureMockMvc
public class MemberControllerTest {

    @Autowired // 의존성 주입방법 중 하나
    private MockMvc mockMvc;
    @Autowired
    private Gson gson;;

    @DisplayName("CONTROLLER : member get 테스트")
    @Test
    void getMemberTest() throws Exception {
        //1. given
        MemberPostDto post = new MemberPostDto("jy@email.com","ㅂㅂㅂ","010-1234-5678");
        String content = gson.toJson(post);

        ResultActions actions =
                mockMvc.perform(
                        post("/rv1/members") //HTTP POST METHOD와 request URL을 설정
                                .accept(MediaType.APPLICATION_JSON)
                                .contentType(MediaType.APPLICATION_JSON)
                                .content(content)
                );
				String location = actions.andReturn().getResponse().getHeader("Location"); // "/v11/members/1"

        //2-2. when-then
				mockMvc.perform(
                        get(location)     
                                .accept(MediaType.APPLICATION_JSON)
                )
                .andExpect(status().isOk())   
                .andExpect(jsonPath("$.data.email").value(post.getEmail()))  
                .andExpect(jsonPath("$.data.name").value(post.getName()))     
                .andExpect(jsonPath("$.data.phone").value(post.getPhone())); 
    }

}
@WebMvcTest //컨트롤러 테스트
public class BookControllerTest {

    @Autowired
    private MockMvc mvc; //이건 모키토 프레임워크 아님

    @MockBean //mokito annotation. import package 위치 확인
    private BookService bookService;

    //테스트 코드는 리턴타입 : void 메서드 내부에서 테스트 하고 종료
    @DisplayName("CONTROLLER : BOOk LIST를 반환한다")
    @Test //테스트 하는 영역이라고 명시
    public void Book_MVC_Test() throws Exception{
        //1. given : 테스트하기 위해서 필요한 데이터 등의 준비물들을 만들어 둔다
        Book book = new Book("book", LocalDateTime.now());
        //org 모키토 import
        given(bookService.getBookList()).willReturn(Collections.singletonList(book)); //가정해서 갖고 있음.
        //book 구현체가 없지만 시그니처를 만들어주어서 가정하고 테스트

        //2. when : 테스트 해야 하는 내용
        ResultActions resultActions = mvc.perform(get("/books"));
                //결과를 반환받는 객체

        //3. then : 검증(verify) 예상했던 결과의 참/거짓 여부를 판단하는 부분
        resultActions
                .andExpect(status().isOk())
                .andExpect(view().name("book"))
                .andExpect(model().attributeExists("bookList"))
                .andExpect(model().attribute("bookList", contains(book)));
    }

DataAccess 계층 테스트: Repository @DataJpaTest

테스트 시작 전 DB 상태를 깨끗하게 만든다.

@DataJpaTest : @Transactional 애너테이션을 포함. 하나의 테스트 케이스 실행이 종료되는 시점에 데이터베이스에 저장된 데이터는 rollback 처리

@DataJpaTest   
public class MemberRepositoryTest {
    @Autowired
    private MemberRepository memberRepository;   // DI

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

        // when 
        Member savedMember = memberRepository.save(member);

        // then
        assertNotNull(savedMember); 
        assertTrue(member.getEmail().equals(savedMember.getEmail()));
        assertTrue(member.getName().equals(savedMember.getName()));
        assertTrue(member.getPhone().equals(savedMember.getPhone()));
    }
}
@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())); 
    }
}