Spring Boot单元测试中数据源Mock配置与实现技术咨询
Spring Boot Controller单元测试:数据源Mock与验证指南
嘿,我来帮你一步步搞定这个Spring Boot单元测试里的数据源Mock和验证问题。你已经搭好了基础的测试配置,接下来咱们把Mock逻辑补全,再加上验证步骤就齐活了。
1. 编写Repository的Mock逻辑
首先在你的setupMock方法里,咱们先准备测试用的实体数据,然后用Mockito来Stub(打桩)Repository的方法,让它返回咱们预设好的数据,而不是真的去连接数据库。
示例代码:
import java.util.Arrays; import java.util.Optional; import org.mockito.Mockito; import org.springframework.test.annotation.Before; // 你的测试类已有的配置... public class ControllerTests { @Autowired private MockMvc mvc; @MockBean private ProductRepository productRepo; @MockBean private MonitorRepository monitorRepo; @Before public void setupMock() { // 1. 准备Product测试数据 Product testProduct = new Product(); testProduct.setId(1L); testProduct.setName("Test Wireless Headphones"); testProduct.setPrice(199.99); List<Product> testProductList = Arrays.asList(testProduct); // 2. Stub ProductRepository的常用方法 // 当调用findAll()时,返回预设的商品列表 Mockito.when(productRepo.findAll()).thenReturn(testProductList); // 当调用findById(1L)时,返回包含测试商品的Optional Mockito.when(productRepo.findById(1L)).thenReturn(Optional.of(testProduct)); // 当查询不存在的ID时,返回空Optional Mockito.when(productRepo.findById(999L)).thenReturn(Optional.empty()); // 3. 同理处理MonitorRepository Monitor testMonitor = new Monitor(); testMonitor.setId(100L); testMonitor.setBrand("Dell"); testMonitor.setSize(27); Mockito.when(monitorRepo.findById(100L)).thenReturn(Optional.of(testMonitor)); } // 后面的测试方法写在这里... }
2. 编写Controller请求测试与验证
接下来咱们用MockMvc发送HTTP请求,验证接口返回的JSON响应是否符合预期,同时还要验证Repository的方法是否被正确调用(确保Controller逻辑和Repository的交互是对的)。
2.1 测试GET接口返回JSON
import org.junit.Test; import org.springframework.http.MediaType; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.mockito.Mockito.verify; @Test public void testGetAllProducts() throws Exception { // 发送GET请求到/api/products mvc.perform(get("/api/products") .contentType(MediaType.APPLICATION_JSON)) // 验证响应状态码是200 OK .andExpect(status().isOk()) // 验证JSON响应里第一个商品的ID和名称 .andExpect(jsonPath("$[0].id").value(1L)) .andExpect(jsonPath("$[0].name").value("Test Wireless Headphones")); // 验证ProductRepository的findAll()方法被调用了一次 verify(productRepo, Mockito.times(1)).findAll(); } @Test public void testGetProductById() throws Exception { // 测试存在的商品ID mvc.perform(get("/api/products/1") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value(1L)); // 验证findById(1L)被调用了一次 verify(productRepo, Mockito.times(1)).findById(1L); // 测试不存在的商品ID mvc.perform(get("/api/products/999") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isNotFound()); // 验证findById(999L)被调用了一次 verify(productRepo, Mockito.times(1)).findById(999L); }
2.2 测试POST/UPDATE/DELETE接口(如果有)
如果你的Controller有新增、修改或删除接口,咱们同样可以Mock Repository的save、deleteById等方法,然后验证请求和方法调用:
import com.fasterxml.jackson.databind.ObjectMapper; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.mockito.Mockito.when; import static org.mockito.Mockito.argThat; @Test public void testCreateProduct() throws Exception { // 准备要新增的商品数据 Product newProduct = new Product(); newProduct.setName("New Bluetooth Speaker"); newProduct.setPrice(89.99); // Stub save方法,返回带ID的保存后对象 Product savedProduct = new Product(); savedProduct.setId(2L); savedProduct.setName("New Bluetooth Speaker"); savedProduct.setPrice(89.99); when(productRepo.save(argThat(product -> product.getName().equals("New Bluetooth Speaker") && product.getPrice() == 89.99))) .thenReturn(savedProduct); // 发送POST请求 mvc.perform(post("/api/products") .content(new ObjectMapper().writeValueAsString(newProduct)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$.id").value(2L)); // 验证save方法被调用了一次 verify(productRepo, Mockito.times(1)).save(Mockito.any(Product.class)); }
3. 关键注意点
@MockBean的作用:它会自动替换Spring容器中的真实Repository Bean,所以你的测试完全不会触达真实数据库,所有数据源交互都是Mock的。@Before的作用:这个注解的方法会在每个测试方法执行前运行,保证每个测试都有干净的Mock状态,不会互相干扰。- Mock重置:默认情况下,每个测试方法都会重新创建
@MockBean对象,所以大部分时候不需要手动重置。如果需要,可以在setupMock开头加上Mockito.reset(productRepo, monitorRepo);。 - 验证方法调用:除了
times(1),还可以用never()(方法从未被调用)、atLeastOnce()(至少被调用一次)等匹配器,灵活验证交互逻辑。
内容的提问来源于stack exchange,提问作者Evgeni Dimitrov
相关产品推荐
相关产品推荐

