@Mock, @InjectMocks ,@MockBean

강찬우·2023년 11월 21일

혼자공부

목록 보기
9/14

Mock으로 테스트 작성 중 발생한 문제와 해결 과정

안녕하세요! 이번에는 코드 테스트 작성 중에 마주한 문제와 그에 대한 해결 과정에 대해 공유하려고 합니다.

문제 상황

테스트 코드를 작성하면서 Mock 객체를 활용하는 중에 예상치 못한 오류가 발생했습니다. 초기 코드는 다음과 같습니다.


@SpringBootTest
@AutoConfigureMockMvc
class QuestionControllerTest {

    @Autowired
    MockMvc mockMvc;

    @InjectMocks
    QuestionService questionService;

	@Mock
    QuestionRepository questionRepository;

    @Test
    void detail() throws Exception {

        Question mockedQuestion = new Question();
        mockedQuestion.setId(1);
        mockedQuestion.setContent("Test content");
        mockedQuestion.setSubject("ds123");
        when(questionRepository.findById(1)).thenReturn(Optional.of(mockedQuestion));
    	when( questionService.getQuestion(1)).thenReturn(mockedQuestion);


        mockMvc.perform(MockMvcRequestBuilders.get("/question/detail/{id}",1))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.view().name("question_detail"))
                .andExpect(MockMvcResultMatchers.model().attributeExists("question"));

    }

}

처음에 코드를 이렇게 작성했는데 이런 오류가 발생했습니다.

@SpringBootTest
class QuestionServiceTest {
    @InjectMocks
    QuestionService questionService;

    @Mock
    QuestionRepository questionRepository;


    @Test
    void test1(){
        //given
        Question question1 = new Question();
        question1.setId(1);
        question1.setContent("chan");
        question1.setSubject("Subject 1");
        question1.setCreateDate(LocalDateTime.now());

        Question question2 = new Question();
        question2.setId(1);
        question2.setContent("chan");
        question2.setSubject("Subject 2");
        question2.setCreateDate(LocalDateTime.now());
        when(questionRepository.findAll()).thenReturn(Arrays.asList(question1, question2));

        //when
        List<Question> result = questionService.getList();
        //then
        Assertions.assertThat(result).isEqualTo(questionRepository.findAll());
        Assertions.assertThat(2).isEqualTo(result.size()); // 예상되는 결과의 크기는 2여야 함
        Assertions.assertThat("Subject 1").isEqualTo(result.get(0).getSubject());
        Assertions.assertThat("Subject 2").isEqualTo(result.get(1).getSubject());


    }

    @Test
    void test2() {

        //given
        Question question1 = new Question();
        question1.setId(1);
        question1.setContent("chan");
        question1.setSubject("Subject 1");
        question1.setCreateDate(LocalDateTime.now());
        when(questionRepository.findById(1)).thenReturn(Optional.of(question1));

        //when
        Question question = questionService.getQuestion(1);

        //then

        Assertions.assertThat(question.getId()).isEqualTo(1);
        Assertions.assertThat(question.getSubject()).isEqualTo("Subject 1");

    }

    @Test
    void test3() {
        when(questionRepository.findById(any())).thenReturn(Optional.empty());
        org.junit.jupiter.api.Assertions.assertThrows(DataNotFoundException.class,()-> questionService.getQuestion(2));
        verify(questionRepository, times(1)).findById(eq(2));
    }


}

서비스에 대한 테스트는 이렇게 작성했을때 잘 작동해서 똑같은 방식으로 진행했는데 발생한 오류라 당황스러웠다.

처음에는

when(questionRepository.findById(1)).thenReturn(Optional.of(mockedQuestion));
when( questionService.getQuestion(1)).thenReturn(mockedQuestion);

이 부분에서 when을 두번쓰는 과정에서 오류가 있나 생각했지만 생각해보니 어차피 mock함수를 쓰면 함수의 값을지정해서 리턴해주기에 이렇게 할 필요도 없었고 이 때문도 아니였다.

그래서 위의 when절을 삭제하고 questionService의 어노테이션을 @InjectMocks에서 @Mock으로 바꾸고 실행헀는데 controller에서 quetion의 값이 null로 나와 내가 지정한 DataFoundException이 발생했다.

답은 @MockBean이였다.

@SpringBootTest
@AutoConfigureMockMvc
class QuestionControllerTest {

    @Autowired
    MockMvc mockMvc;

    @MockBean
    QuestionService questionService;

    @Test
    void detail() throws Exception {

        Question mockedQuestion = new Question();
        mockedQuestion.setId(1);
        mockedQuestion.setContent("Test content");
        mockedQuestion.setSubject("ds123");
        when( questionService.getQuestion(1)).thenReturn(mockedQuestion);

        mockMvc.perform(MockMvcRequestBuilders.get("/question/detail/{id}",1))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.view().name("question_detail"))
                .andExpect(MockMvcResultMatchers.model().attributeExists("question"));

    }

}

이렇게 코드를 수정하니 잘 작동했다. mockMvc에서 주소로 이동하면서 실행된 controller에서 questionservice를 주입받아야되는데 주입받지 못해 오류가 발생한 것 같다.
@MockBean은 테스트 컨텍스트에 Mock 빈을 등록하므로, 해당 빈을 주입받는 다른 빈들은 이 Mock 객체를 사용하게 된다. 이를 통해 외부 의존성을 제어하고, 특정 동작을 가로채어 테스트할 수 있다.

0개의 댓글