@WebMvcTest(PostController.class)classPostControllerTest{@AutowiredprivateMockMvc mockMvc;@MockBean// 실제 PostUseCase 대신 Mock 객체가 컨텍스트에 등록됨privatePostUseCase postUseCase;@Test@DisplayName("게시글 조회 API 테스트")voidgetPost_success()throwsException{// Mock 동작 정의given(postUseCase.findById(1L)).willReturn(newPostResponse(1L,"제목","PUBLISHED"));// When & Then mockMvc.perform(get("/api/posts/1")).andExpect(status().isOk()).andExpect(jsonPath("$.title").value("제목"));}@Test@DisplayName("서비스 예외 → 404 응답 매핑 테스트")voidgetPost_notFound_returns404()throwsException{// 예외를 던지도록 Mock 설정given(postUseCase.findById(999L)).willThrow(newResourceNotFoundException("Post not found: 999")); mockMvc.perform(get("/api/posts/999")).andExpect(status().isNotFound());}}
@SpringBootTestclassPostServiceIntegrationTest{@AutowiredprivatePostService postService;@MockBean// 외부 API 호출 같은 특정 의존성만 MockprivateLinkScrapingAdapter linkScrapingAdapter;@TestvoidcreatePost_withMockedLinkScraping(){given(linkScrapingAdapter.scrape("https://example.com")).willReturn(newLinkMetadata("제목","설명","thumbnail.png"));// 실제 서비스 로직 + Mock 외부 API 조합으로 테스트PostResponse result = postService.createWithLink("https://example.com");assertThat(result.getTitle()).isEqualTo("제목");}}
importstaticorg.mockito.BDDMockito.*;// BDD 스타일 (권장)given(postUseCase.findById(1L)).willReturn(response);// 반환값 설정given(postUseCase.findById(999L)).willThrow(newRuntimeException());// 예외 설정// 인자 매처given(postUseCase.create(any(CreatePostCommand.class))).willReturn(response);given(postUseCase.findByTitle(anyString())).willReturn(List.of());// 검증 — 해당 메서드가 실제로 호출됐는지then(postUseCase).should(times(1)).findById(1L);then(postUseCase).should(never()).deleteById(anyLong());
@SpringBootTestclassPostServiceTest{@SpyBean// 실제 Bean이지만 일부 메서드만 Mock 가능privateEmailService emailService;@TestvoidcreatePost_emailSentOnce(){// 이메일 전송 메서드만 Mock (실제 실행 방지)doNothing().when(emailService).sendNotification(anyString()); postService.create(command);// 이메일이 1번 호출됐는지 검증verify(emailService,times(1)).sendNotification(anyString());}}