22-07-11 ERROR [WebMvcTest 시작부터 에러내기]

김설영·2022년 7월 11일

MyErrorLog

목록 보기
4/6

JSON방식으로 데이터를 주고받는 RESTful API 설계를 해보기로 마음먹으면서, 한 가지 꼭 해보고싶은, 관심가는 작업이 있었다.

그것은 단위테스트이다.

MockMvc객체와 @WebMvcTest를 사용해서 컨트롤러만 따로 테스트해보고 싶었다. 단위만 따로 떼서 테스트한다는 것이 상당히 재밌어보였기 때문이다.

뭐, 결론부터 말하자면, 컨트롤러 설계부터 애먹었다.

분명히 강의 들을때는 아 쉽네 Ez하네~ 하면서 들었는데 정작 작성해보려니까 손이 안떨어지더라. 강의를 듣고 따라치거나, 설명을 이해할 때는 쉬웠는데, 왜 못쓰겠지 싶었다.

그래서 고심끝에 작성한 컨트롤러 코드..

@GetMapping("/")
public String home() {
    return "Hi!";
}

ㅋㅋㅋㅋ

이게 최선이었다… 아직은 타임리프 이용해서 MVC하는게 더 쉬울정도.. 원래도 멀었던 갈길이 더 멀어진 기분이지만..

일단 저거라도 테스트해보자 다짐했다. 일단은 MockMvc를 써보는게 오늘의 목표였으니까!

야심차게 준비한 나의 컨트롤러 테스트 코드이다.

@WebMvcTest(ArticleController.class)
class ArticleControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    @DisplayName("/ 요청 시 Hi! 를 출력한다.")
    void test1() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("Hi!"));

    }
}

그런데, 가장 먼저 맞이한 에러..(라고하기엔 애매한)

이거 뭐 심각한 에러인가 싶어서 열심히 구글링을 했다. IntelliJ에도 비슷한 문의가 있었는지, 이에 대한 답변이 있었는데, 뭐 결론은 그걸로 해결은 하지 못했고, (옛날 글이라 Preferences의 구성이 살짝 달랐다. 이것저것 만져보았으나 실패했다.) 이거 큰일이다 싶어서 여러 방면으로 검색을 해봤지만, 모두 Junit4의 문제를 다룰 뿐, 나와 같은 현상을 겪는 사람은 없었..

지 않다. 인프런에 같은 현상을 겪는 사람이 질문을 올렸다. 그 질문에서 얻어낸 것.

“저렇게 에러 체크가 되어도, 실행하면 실행이된다"는 것. 그렇다. 꼴뵈기 싫긴 하지만 일단 실행을 해보기로 했다.

그래서 나온 결과.

뭐가 문제지? 하고 저 무서운 빨간 글씨를 전부 읽긴 어려우니 Caused by 부분을 자세히 읽어보았는데, 두 가지 문제가 있었다.

  1. UnsatisfiedDependencyException: Error creating bean with name 'articleController’
  2. NoSuchBeanDefinitionException: No qualifying bean of type 'ToyProject.RestfulVelog.service.ArticleService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

1번에는, articleController 라는 이름으로 된 빈이 생성이 안되었다는 것. 이유는 “의존성이 만족되지 않아서"라고 써있다.

2번에는, 빈에 대한 정의를 찾을 수 없다(?), 적합한 빈 타입이 없다는 것. 적어도 ArticleService라는 빈은 적어도 1개의 Autowire 후보가 있는데, 아무것도 없다고 말해주고 있다.

즉, 해당 테스트는 컨트롤러에 접근해서 수행되는데, 내 진짜 컨트롤러에는 ArticleService가 DI되어있다.

그런데, 테스트 환경에는 해당 컨트롤러에 DI를 해줄 객체가 없다는 것 이라고 해석했다.

@RestController
@RequiredArgsConstructor
public class ArticleController {

    private final ArticleService articleService;

    @GetMapping("/")
    public String home() {
        return "Hi!";
    }

    @PostMapping("/post-article")
    public void postArticle(@RequestBody @Validated ArticleDto articleDto) {
        articleService.saveArticle(articleDto);
    }
}

그래서, 테스트 환경에도, 컨트롤러가 DI로 이용할 객체를 만들어줘야 한다고 생각했다.

가장 쉬운 방법은 @SpringBootTest 애노테이션을 붙이고, @Autowired로 ArticleService 객체를 DI 해주는 것이겠지만, 그럼 단위 테스트를 구성한 의미가 없으니 다른 방법으로 DI 조건을 만족시켜줘야 한다고 생각했다.

그래서, 구글링을 한 결과, 객체도 “가짜 객체"를 만들어 의존성 주입을 해줄 수 있다고 한다.

방법은 매우 간단했다. 기존에 사용하던 @Autowired 대신 @MockBean을 붙여주면 됐다. 아래와 같이 코드를 작성해주어 해결했다.

@WebMvcTest
class ArticleControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private ArticleService articleService;

    @Test
    @DisplayName("/ 요청 시 Hi! 를 출력한다.")
    void test1() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("Hi!"));

    }
}

RESTful Api에 대한 감을 잡을 때 까지, 문제를 계속 마주할 것이다. 이렇게 오래걸리더라도, 하나하나 없애나가보자!

profile
블로그 이동하였습니당! -> https://kimsy8979.tistory.com/

0개의 댓글