[53일차]API계층테스트,데이터엑세스계층테스트

유태형·2022년 7월 13일
0

코드스테이츠

목록 보기
53/77

오늘의 목표

  1. API계층 테스트
  2. 테이터엑세스계층 테스트



내용

API계층 테스트

애플리케이션은 3개의 계층으로 나뉘어 져 있고 각 계층별로 역할이 다르며, 서로 연동되기 때문에 계층별로 문제가 없는지 확인하는 테스트 작업이 필요합니다.

단위 테스트 만으로는 특정 모듈이나 기술에 의존적인 애플리케이션의 모든 기능이 정상적으로 작동된다고 보장하기는 어렵습니다.

특정 계층만 잘라서 테스트하는 것을 슬라이스 테스트(Slice Test)라고합니다.

API계층의 Controller들을 테스트하면 API계층을 점검할 수 있습니다.

@SpringBootTest
@AutoConfigureMockMvc
public class 테스트{
	@Autowired
    private MockMvc mockMvc;
    
    @Test
    public void 테스트케이스(){
    	//given
        //when
        //then
    }
}
  • @SpringBootTest : SpringBoot 기반의 Application Context를 생성합니다. Application Context에는 Bean객체들이 등록되어 있습니다.

  • @AutoConfigureMockMvc : 테스트에 필요한 에플리케이션의 설정을 자동을 구성해 줍니다.

  • MockMvc : 서버를 실행시키지 않고 Spring의 Controller를 테스트할 수 있는 환경을 지원해주는 프레임워크입니다.

responseBody응답 데이터에 포함된 한글이 개지는 경우 applicatoin.yml파일의 하단에 설정을 추가합니다.

...

server:
	servlet:
    	encoding:
        	force-response: true

Request로 보낸 데이터와, 저장후 응답으로 받은 데이터가 동일한지 테스트하는 테스트케이스는 MockMvc를 이용하여 간편하게 테스트 할 수 있습니다.

@SpringBootTest
@AutoConfigureMockMvc
class 테스트{
	@Autowired
    private MockMvc mockMvc;
    
    @Autowired
    private Gson gson;
    
    @Test
    void 테스트케이스() throws Exception{
    	//given
        Dto.Post post = new Dto.Post(매개변수1,매개변수2,매개변수3);
        String content = gson.toJson(post);
        
        //when
        ResultActions actions =
        		mockMvc.perform(
                					post("경로")
                                    	.accept(MediaType.APPLICATION_JSON)
                                        .contentType(MediaType.APPLICATION_JSON)
                                        .content(content)
                				);
                                
                                
        //given
        MvcResut result = actions
        					.andExpect(status().isCreated())
                            .andExpect(jsonPath("$.data.매개변수1").value(post.get매개변수1()))
                            .andExpect(jsonPath("$.data.매개변수2").value(post.get매개변수2()))
                            .andExpect(jsonPath("$.data.매개변수3").value(post.get매개변수3()))
                            .andReturn();
                            
        System.out.println(result.getResponse().getContentAsString());
    }
}
  • Gson : 자바 객체를 JSON으로 손쉽게 전환할 수 있는 메서드를 제공합니다.

  • MockMvc.perform() : 실제로 HTTP전송하는 것처름 MockMvc가 테스트용으로 HTTP 요청을 전송합니다. Http Response를 반환합니다.

  • post("uri") : POST HTTP메서드를 전송합니다.

  • accept(MediaType.APPLICATION_JSON) : JSON형식으로 데이터를 읽어들입니다.

  • contentType(MediaType.APPLICATION_JSON) : JSON형식으로 HTTP request Body를 전송합니디ㅏ.

  • content() : Http Request Body에 해당 내용을 첨부합니다.

  • ResultActions : HTTP Request에 대한 HTTP Response를 가지며 다양한 메서드를 제공합니다.

  • status() : 응답 상태를 반환합니다.

  • andExpect() : 해당 값이 참인지 확인합니다. 참이면 다음 Expect를 실행하고 거짓이면 failed 처리합니다.

  • jsonPath("$.A.B.C") : $는 최상위 객체를 나타냅니다. .으로 하위 객체나 필드로 이동할 수 있습니다. 실제로 존재해야하며 없을시 에러 발생합니다.

  • value() : Hamcrest의 Assertions이며 해당 값이 동일한지 비교합니다.

  • andReturn() : andExpect() 수행후 결과를 반환합니다.

Controller의 메서드만을 이용하여 테스트를 진행하지만, 완벽하게 슬라이스 테스트라고 보기엔 힘듭니다. 왜냐하면 Controller의 메서드 안에서 이미 Service의 메서드를 호출하고 Service에서 Repository를 호출하니 다른 계층의 메서드도 호출하기 때무입니다.

이 문제는 Mock 객체를 사용해 계층간의 연결을 끊어줌으로써 해결할 수 있습니다.




데이터엑세스계층 테스트

데이터 엑세스 계층의 테스트는 무슨 기술을 사용하고 있느냐에 따라 달라질 수 있습니다. 저는 현재 Spring Data JPA를 사용중이므로 해당 기술에 맞게 데이터 엑세스 계층 테스트를 진행하였습니다.

데어터 엑세스 계층은 데이터베이스를 사용하므로 테스트 후 실행 이전으로 깨끗히 되돌리는 것이 중요합니다.

테스트 케이스 실행 결과가 데이터베이스에 반영 되어도 안되고, 테스트 케이스 실행 순서가 테스트 케이스 실행 결과에 영향을 주어서는 안됩니다.

@DataJpaTest
public class 레포지토리테스트{
	@Autowired
    private 레포지토리 레포지토리;
    
    @Test
    public void test1(){
    	//given
        엔티티 엔티티 = new 엔티티();
        엔티티.set필드1(...);
        엔티티.set필드2(...);
        엔티티.set필드3(...);
        //when
        엔티티 saved엔티티 = 레포지토리save(엔티티);
        //then
        assertNotNull(saved엔티티);
        assertTrue(엔티티.get필드1().equals(svaed엔티티.get필드1()));
        assertTrue(엔티티.get필드2().equals(svaed엔티티.get필드2()));
        assertTrue(엔티티.get필드3().equals(svaed엔티티.get필드3()));
    }
    
    @Test
    public void test2(){
    	//given
        엔티티 엔티티 = new 엔티티();
        엔티티.set필드1(...);
        엔티티.set필드2(...);
        엔티티.set필드3(...);
        //when
        레포지토리.save(엔티티);
        Optional<엔티티> find엔티티 = 레포지토리.findBy필드(엔티티.get필드());
        //then
        assertTrue(find엔티티.isPresent());
        assertTrue(find엔티티.get().get필드().equals(엔티티.get필드()));
    }
}

두 테스트케이스 모두 입력시 사용한 객체와 레포지토리에 등록 혹은 검색후 반환받은 객체의 필드를 비교함으로써 같은 객체인지 테스트를 수행하고 있습니다.

@DataJpaTest 에너테이션은 @Transactional에너테이션을 내장하고 있습니다. @Transactional에너테이션은 클래스에 적용하면 모든 메서드를 각각 트랜잭션으로 묶어주고 모두 성공하거나 모두 실패합니다(All or Nothing).테스트 케이스는 테스트 수행후 데이터베이스를 원래상태로 되돌려 주어야 하므로 항상 Nothing할 수 있도록 항상 rollback시켜 줍니다.

test1()test2()는 각각 원래 상태의 데이터베이스를 참조하여 테스트 수행후 원래상테로 데이터베이스를 되돌리고 마무리합니다.




후기

단위테스트를 좀더 넓혀 계층 단위로 슬라이스 테스트를 수행하였습니다. 연관된 기술이나 다른 계층도 신경 써야 하므로 좀더 섬세하게 환경을 가정하는것이 세삼 중요하다는것을 몸소 겪었습니다.




GitHub

private!

profile
오늘도 내일도 화이팅!

0개의 댓글