Spring Boot中自制JPA Repository Mock实现与测试方案咨询
不借助Mock框架自制Repository Mock测试Spring Service
我太懂你不想依赖Mockito、EasyMock这些框架的想法了——其实完全可以通过手动实现接口的方式自制Mock,完全不用第三方工具。结合你给出的代码,我给你一步步拆解可行方案:
核心思路
因为CLASSrepo是继承自JpaRepository的接口,我们只需要手动写一个该接口的实现类,用内存集合模拟数据库的存储和操作,完全自己控制返回结果、业务逻辑甚至异常场景。
第一步:编写Mock Repository实现类
这个类会完全模拟数据库的CRUD操作,用内存List代替真实数据库:
// 手动实现CLASSrepo接口,作为Mock Repository public class MockCLASSrepo implements CLASSrepo { // 用内存List模拟数据库表 private final List<CLASS> mockDatabase = new ArrayList<>(); private long nextAutoIncrementId = 1; // 模拟根据ID查询 @Override public Optional<CLASS> findById(Long id) { return mockDatabase.stream() .filter(clazz -> clazz.getId().equals(id)) .findFirst(); } // 模拟保存/更新操作 @Override public <S extends CLASS> S save(S entity) { if (entity.getId() == null) { // 模拟自增ID entity.setId(nextAutoIncrementId++); mockDatabase.add(entity); } else { // 模拟更新逻辑:先删后加 mockDatabase.removeIf(clazz -> clazz.getId().equals(entity.getId())); mockDatabase.add(entity); } return entity; } // 模拟查询所有 @Override public List<CLASS> findAll() { // 返回副本避免外部直接修改内存数据 return new ArrayList<>(mockDatabase); } // 模拟删除操作 @Override public void deleteById(Long id) { mockDatabase.removeIf(clazz -> clazz.getId().equals(id)); } // 其他未用到的JpaRepository方法,可以直接抛异常或留空 @Override public long count() { throw new UnsupportedOperationException("本Mock未实现count方法"); } // 剩下的JpaRepository方法同理,根据你的Service实际使用情况实现即可 }
第二步:优化Service的注入方式(可选但强烈推荐)
原来的Service用字段注入@Autowired不利于测试,改成构造器注入(Spring 4.3+可以不用加@Autowired),这样测试时能直接传入Mock对象:
@Service public class CLASSservice { private final CLASSrepo classRepo; // 构造器注入,替代字段上的@Autowired public CLASSservice(CLASSrepo classRepo) { this.classRepo = classRepo; } // 你的业务方法示例 public CLASS saveClass(CLASS clazz) { return classRepo.save(clazz); } public Optional<CLASS> getClassById(Long id) { return classRepo.findById(id); } public void deleteClass(Long id) { classRepo.deleteById(id); } }
如果不想修改原有Service,也可以给classRepo加一个setter方法,测试时通过setter注入Mock。
第三步:编写测试类,手动注入Mock
完全不用Spring Test的@MockBean或任何框架,直接手动实例化Mock和Service:
import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class CLASSserviceTest { @Test void testSaveAndRetrieveClass() { // 1. 实例化自制的Mock Repository MockCLASSrepo mockRepo = new MockCLASSrepo(); // 2. 把MockRepo注入到Service(构造器注入直接传参) CLASSservice service = new CLASSservice(mockRepo); // 3. 测试业务逻辑 CLASS testClass = new CLASS(); testClass.setName("测试班级"); // 假设CLASS有name字段 // 保存测试数据 CLASS savedClass = service.saveClass(testClass); assertNotNull(savedClass.getId()); assertEquals("测试班级", savedClass.getName()); // 根据ID查询 Optional<CLASS> foundClass = service.getClassById(savedClass.getId()); assertTrue(foundClass.isPresent()); assertEquals(savedClass.getId(), foundClass.get().getId()); } @Test void testDeleteClass() { MockCLASSrepo mockRepo = new MockCLASSrepo(); CLASSservice service = new CLASSservice(mockRepo); CLASS testClass = new CLASS(); testClass.setName("待删除班级"); CLASS saved = service.saveClass(testClass); Long classId = saved.getId(); // 执行删除 service.deleteClass(classId); // 验证删除结果 Optional<CLASS> deletedClass = service.getClassById(classId); assertFalse(deletedClass.isPresent()); } }
额外技巧:模拟异常场景
如果需要测试Service的异常处理逻辑,直接在MockRepo里模拟异常即可:
// 在MockCLASSrepo的save方法中添加异常逻辑 @Override public <S extends CLASS> S save(S entity) { if (entity.getId() != null && entity.getId().equals(-1L)) { // 模拟数据库保存失败的异常 throw new RuntimeException("模拟数据库连接失败"); } // 正常保存逻辑... }
对应的测试用例:
@Test void testSaveWithException() { MockCLASSrepo mockRepo = new MockCLASSrepo(); CLASSservice service = new CLASSservice(mockRepo); CLASS errorClass = new CLASS(); errorClass.setId(-1L); errorClass.setName("异常测试班级"); // 验证Service是否正确处理了异常 assertThrows(RuntimeException.class, () -> service.saveClass(errorClass)); }
内容的提问来源于stack exchange,提问作者cwiq
相关产品推荐
相关产品推荐

