我想从控制器和服务层测试相同的方法。问题是:为什么我必须在控制器中使用@MockBean
注释,为什么不为@Mock
使用BookFindOperationsService bookService
注释。服务方面的问题相同,为什么我需要@Mock
存储库,为什么不使用@MockBean
?你能给我两者之间的区别吗?
这是控制者:
@RestController
public class BookFindOperationsController {
private final BookFindOperationsService bookService;
@Autowired
public BookFindOperationsController(BookFindOperationsService bookService) {
this.bookService = bookService;
}
@GetMapping("/books/author/{authorID}")
public List<Book> findBooksByAuthor(@PathVariable String authorID) {
return bookService.findBooksByAuthor(authorID);
}
}
这是服务类别:
@Service
public class BookFindOperationsService {
private final BookRepository bookRepository;
@Autowired
public BookFindOperationsService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
public List<Book> findBooksByAuthor(String authorID) {
return bookRepository.findByAuthorAllIgnoreCase(authorID);
}
}
服务测试:
@RunWith(MockitoJUnitRunner.class)
public class BookFindOperationsServiceTest {
@Mock
BookRepository bookRepository;
@InjectMocks
BookFindOperationsService bookFindOperationsService;
@Test
public void findBooksByAuthor() {
Book book = createDummyBook();
List<Book> books = new ArrayList<>();
books.add(book);
when(bookRepository.findByAuthorAllIgnoreCase("Henryk Sienkiewicz")).thenReturn(books);
assertEquals(1, bookFindOperationsService.findBooksByAuthor("Henryk Sienkiewicz").size());
}
private Book createDummyBook() {
return new Book("W pustyni i w puszczy", "Henryk Sienkiewicz", "dramat", true);
}
控制器测试:
@RunWith(SpringRunner.class)
@WebMvcTest(BookFindOperationsController.class)
public class BookFindOperationsControllerTest {
@Autowired
MockMvc mockMvc;
@MockBean
BookFindOperationsService bookService;
@Test
public void findBooksByAuthor() throws Exception {
List<Book> books = new ArrayList<>();
Book book = new Book("W pustyni i w puszczy", "Henryk Sienkiewicz", "dramat", true);
books.add(book);
when(bookService.findBooksByAuthor("Henryk Sienkiewicz")).thenReturn(books);
String expected = "[{\"id\":0,\"title\":\"W pustyni i w puszczy\",\"author\":\"Henryk Sienkiewicz\",\"category\":\"dramat\",\"available\":true}]";
MvcResult mvcResult = mockMvc.perform(get("/books/author/Henryk Sienkiewicz")
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andReturn();
String content = mvcResult.getResponse().getContentAsString();
assertEquals(expected, content);
verify(bookService, times(1)).findBooksByAuthor(anyString());
}
答案 0 :(得分:3)
@Mock
和@MockBean
之间的主要区别在于,前者属于Mockito框架,而后者属于Mockito下的Spring Test Framework。
@MockBean
用一个模拟的豆创建/替换一个弹簧豆,以便其他弹簧加载的豆(控制器等)可以使用它。这就是为什么在使用MockMvc.perform
时需要它的原因。
@Mock
注释不适用于Spring Context。它将仅尝试将标有此批注的模拟对象映射到标有@InjectMocks
的对象的属性。
更新
使用@WebMvcTest(BookFindOperationsController.class)
时,将创建一个Spring Context,该上下文具有所有必需的bean,以支持将BookFindOperationsController
类作为Web应用程序运行。这意味着任何拦截器,过滤器,转换器也需要在Spring Context中加载。这个Spring Context还将加载自己的BookFindOperationsService
,它不是模拟的而是实际的实现。但是对于测试,您需要一个Mock,这就是为什么要用@MockBean
对该bean进行注释,以指示Spring Context使用Mocked而不是Actual的原因。这是一个集成测试,因为您可以测试所有组件是否可以正常协同工作。
使用@RunWith(MockitoJUnitRunner.class)
时,不会隐式创建任何Spring Context。因此,您不能使用@MockBean
,因为没有要模拟的Spring Bean。这是一个单元测试,因为您仅在模拟BookFindOperationsService
时测试BookRepository
,而没有在数据库上实际保存任何内容。
希望这很好地解释了。
答案 1 :(得分:0)
@MockBean用于将模拟对象添加到Spring应用程序上下文中。该模拟将替换应用程序上下文中任何现有的相同类型的bean。您可以在集成测试中使用它
@Mock在单元测试中用于替换某些实现。 看here
使用@MockBean时的一个重要说明。在这种情况下,不会缓存spring上下文,并且如果您有很多集成测试,则上下文的初始化可能会花费很多时间。