꾸준히하자아자

stub는 언제 사용할까? 본문

Backend/Spring Boot

stub는 언제 사용할까?

성장하고픈개발자 2023. 7. 26. 03:27
728x90
728x90

컨트롤러 단위테스트를 진행하면서 처음에 혼란이 왔던 부분은 stub는 언제? 왜? 사용하는지가 의문이었다.

근데 정확히 배우고 나니까 너무 간단한 문제였던 것,,,,

내가 이해한 내용을 토대로 작성해보겠다.

 

Stub이란?

stub은 상태 검증 이라고 할 수 있다. 상태 검증이란 메소드가 수행된 후, 객체의 상태를 확인하여 올바르게 동작했는지를 확인하는 검증법이다.

다시 정리하자면, 컨트롤러에는 서비스를 호출하는 메서드가 존재하는데, 그 메서드의 리턴값을 내가 임의로 지정해주는 것이다.

 

나는 "컨트롤러" 단위테스트를 작성하는 것이 목표였어서 UserService를 MockBean으로 등록 하였다.

사실상 UserService 안에 있는 메서드 로직을 사용할 수 없는 상태인거다.

따라서 난 컨트롤러에서 쓰인 UserService 의 메서드를 stubbing 하여 내가 직접 리턴 값을 만들어줘야 했다.

 

로그인 테스트를 작성할 때, 서비스 계층에서 jwt 토큰을 반환해주는 로직이 필요한데 이 부분을 stubbing 하여 내가 직접 반환값을 만들어주었다.


아래 코드는 Post 요청 /login 컨트롤러의 일부

서비스 계층에서 jwt를 리턴하고, 또 컨트롤러에서 jwt를 리턴하는 것을 볼 수 있다.

String jwt = userService.login(requestDTO);
            return ResponseEntity.ok().header(JWTProvider.HEADER, jwt).body(ApiUtils.success(null));

아래 코드는 login_test() 테스트의 일부

   @Test
    public void login_test() throws Exception {
        // given
        UserRequest.LoginDTO loginDTO = new UserRequest.LoginDTO();
        loginDTO.setEmail("aaa@nate.com");
        loginDTO.setPassword("aaa1234!");
                
        String requestBody = om.writeValueAsString(loginDTO);
        System.out.println("테스트 : "+responseBody);

        // stub
        User user = User.builder().id(1).roles("ROLE_USER").build(); //user 객체 만듬

        String jwt = JWTProvider.create(user); //jwt 토큰 생성 (더미 생성)

        Mockito.when(userService.login(any())).thenReturn(jwt); 
        //만약에 userService의 login()이 실행되면 무엇을 넣던 jwt가 리턴되게 내부구현

컨트롤러로 요청 올 DTO를 임의로 만들고 User 객체를 만들고 직접 만든 토큰 생성기 JWTProvider 이라는 클래스로 더미 데이터 jwt 를 생성하고 저장한다. 그리고 userService 에서 사용된 login 메서드의 반환값을 다음과 같이 지정해주었다. 

 

주석에 쓰여있는 것과 같이 만약에(가정법) UserService의 login() 메서드가 실행되면 login() 메서드의 파라미터에 무슨 값이 들어가던, 위에 생성해 주었던 더미 jwt 토큰 값을 반환하게 해 주었다.

이것이 stubbing !!


또 다른 예시를 들자면, 전체 상품 목록을 조회하는 ProductRestController 테스트 코드를 작성하였는데

아직 Service를 작성하기 전이어서 더미데이터를 생성해두었던 FakeStore을 MockBean으로 등록하였다.

 

아래는 findAll_test() 의 일부

@Import({
        SecurityConfig.class
})
@WebMvcTest(controllers = {ProductRestController.class})
class ProductRestControllerTest {

    @MockBean
    FakeStore fakeStore;

    @Autowired
    private MockMvc mvc;
    
    @Test
    public void findAll_test() throws Exception {
        //given
		int parameter = 0;

        //stub
        List<Product> productList = new ArrayList<>();
        Product p1 = new Product(1, "기본에 슬라이딩 지퍼백 크리스마스/플라워에디션 에디션 외 주방용품 특가전", "", "/images/1.jpg", 1000);
        Product p2 = new Product(2, "[황금약단밤 골드]2022년산 햇밤 칼집밤700g외/군밤용/생율", "", "/images/2.jpg", 2000);
		productList.add(p1);
        productList.add(p2);

        Mockito.when(fakeStore.getProductList()).thenReturn(productList);

위와 같이 getProductList() 의 반환값을 상품의 리스트 형태로 지정해주기 위해서 위에 상품 2개정도만 더미데이터로 추가로 하드코딩하여 만들어주었다.

fakeStore, 즉 Service에서 호출되는 메소드 getProductList()의 리턴값이 뭐가 되었던 productList 리스트를 반환하도록 stubbing 한 걸 볼 수 있다.

 

노션 정리 + 블로그 정리까지 해서 완벽 이해 완료...^^

728x90
728x90