안녕하세요! 이번에는 코드 테스트 작성 중에 마주한 문제와 그에 대한 해결 과정에 대해 공유하려고 합니다.
테스트 코드를 작성하면서 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 객체를 사용하게 된다. 이를 통해 외부 의존성을 제어하고, 특정 동작을 가로채어 테스트할 수 있다.