준준의 기록일지

[스프링 에러]Controller Test 시 Repository bean 생성 문제 본문

스프링 에러

[스프링 에러]Controller Test 시 Repository bean 생성 문제

junjunwon 2020. 7. 29. 16:44

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."




spring boot Run을 하면 빈 주입부터 실행까지 잘 되는데, Controller Test만 하면 Repository를 불러오지 못하는 문제가 발생했다.

 

기초가 부족해서 Junit4에서 Junit5로도 바꿔보고, 프로젝트도 다시 생성해보고, 별 쇼를 다했고, 빈을 다시 생성해보고 등등 뭐 많이 해봤는데, 문제는 단 ㅎ.....한 줄이였다.

 

 

 

먼저 에러코드는 다음과 같다. Junit 에서는 Failed to load ApplicationContext 에러를 뱉었다.

에러 console

*************************** APPLICATION FAILED TO START *************************** Description: Field repository in com.example.demo.interfaces.RestaurantController required a bean of type 'com.example.demo.domain.RestaurantRepository' that could not be found. The injection point has the following annotations: - @org.springframework.beans.factory.annotation.Autowired(required=true) Action: Consider defining a bean of type 'com.example.demo.domain.RestaurantRepository' in your configuration. 2020-07-29 15:21:08.948 ERROR 34172 --- [ main] o.s.test.context.TestContextManager : Caught exception while allowing TestExecutionListener [org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@366ac49b] to prepare test instance [com.example.demo.interfaces.RestaurantControllerTest@24d09c1] java.lang.IllegalStateException: Failed to load ApplicationContext

 

/*

* 실제로 컨트롤러 webmvctest를 할때 제대로된 저장소를 사용할 수 없다.

* 그래서 이댸 직접 컨트롤러에 의존성 주입을 해줘야 한다. %like% @SpyBean

*/

 

해결방법 = @SpyBean

해당 어노테이션을이용해 Repository 코드를 체크하고 단위 테스트를 진행할 수 있다.

실제 컨트롤러 WebMvcTest를 하면 제대로된 저장소를 사용할 수 없다.

그래서 이때 직접 컨트롤러에 의존성 주입을 해줘야한다.  -> 이때 사용하는 것이 @SpyBean

 

==================================================

@SpyBean

private RestaurantRepository restaurantRepository;

==================================================

 

이렇게 하면 Repsitory를 정상적으로 불러와서 테스트를 진행할 수 있다.

 

만약 여기서 Repository를 구현체와 인터페이스로 구분할 경우 restaurantRepository 실질적인 구현이 없는 인터페이스기 때문에 오류가 발생한다.

-> 이때 아래와 같이 구현체를 명시해주면 오류가 해결된다.

@SpyBean(RestaurantRepositoryImpl.class)

private RestaurantRepository restaurantRepository;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.example.demo.interfaces;
 
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
 
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
 
import com.example.demo.domain.RestaurantRepository;
 
import static org.hamcrest.CoreMatchers.containsString;
 
@RunWith(SpringRunner.class)
@WebMvcTest(RestaurantController.class)
public class RestaurantControllerTest {
    
    @Autowired
    private MockMvc mvc;
    
    /*
     * 실제로 컨트롤러 webmvctest를 할때 제대로된 저장소를 사용할 수 없다.
     * 그래서 이댸 직접 컨트롤러에 의존성 주입을 해줘야 한다. %like% @SpyBean
     */
    @SpyBean
    private RestaurantRepository restaurantRepository;
    
    @Test
    public void list() throws Exception {
        mvc.perform(get("/restaurants"))
            .andExpect(status().isOk())
            .andExpect(content().string(
                    containsString("\"name\":\"bob zip\"")
                    ))
            .andExpect(content().string(
                    containsString("\"id\":1004")
                    ));
    
    }
 
}
 
cs

참고사항 

(출처 : https://tram-devlog.tistory.com/entry/MockBean-SpyBean-%EC%9D%B4%EC%9A%A9-Caching-%EC%82%AC%EC%9A%A9-%EC%8B%9C-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0)

Spring Boot 1.4부터 @MockBean과 @SpyBean이 추가됬다.

- @MockBean은 껍데기만 그 객체의 형태를 유지하고, 내부 구현은 사용자가 when, given이나 기타 등등을 통해서 사용자가 직접 정의해줘야한다.

- @SpyBean을 사용하면 given, when을 한것 외에 실제 객체를 사용한다.