如何使用JUnit测试包含for循环的saveService方法?
如何测试药品计划保存与删除服务?
问题背景
我目前只会测试JPA的planDayRepo.save(planDay)部分,不知道如何测试包含业务逻辑的savePlans方法,以及另一个类似的、通过循环更新pillNumber的删除服务,希望得到测试建议。
待测试的savePlans方法
public String savePlans(List<PlanDay> plans) { for (int i = 0; i < plans.size(); i++) { PlanDay planDay = plans.get(i); long medsId = planDay.getMeds().getMedsId(); int medsToTake = planDay.getMedsToTake(); int pillNumber = medsRepo.getReferenceById(medsId).getPillNumber(); int pillUpdate = pillNumber - medsToTake; Meds updatePlanDay = medsRepo.findById(medsId).get(); if (pillUpdate > 0) { updatePlanDay.setPillNumber(pillUpdate); } else { return "Error: No pills available (Existing Pills: " + pillNumber + ")"; } planDayRepo.save(planDay); } return "Saved Successfully"; }
实体类代码
Meds 实体
@Data @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "meds") @JsonIgnoreProperties(value = { "days" }) public class Meds { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long medsId; @Column private String medsName; @Column private BigDecimal price; @Column private String category; @Column private int pillNumber; @Column private Date medsDate; @OneToMany(mappedBy = "meds", cascade = {CascadeType.REMOVE}, fetch = FetchType.LAZY) @OnDelete(action = OnDeleteAction.CASCADE) private Set<PlanDay> days = new HashSet<PlanDay>(); }
Plans 实体
@Data @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "plans") @JsonIgnoreProperties(value = { "days" }) public class Plans { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long planId; @OneToMany(mappedBy = "plans", fetch = FetchType.LAZY) @OnDelete(action = OnDeleteAction.CASCADE) private Set<PlanDay> days = new HashSet<PlanDay>(); }
PlanDay 实体
@Data @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "planDay") @JsonIgnoreProperties(value = { "planDaysId" }) public class PlanDay { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long planDaysId; @Column @PastOrPresent @FutureOrPresent(groups = OneWeekInTheFuture.class) @Min(value = 2022 - 12 - 22, groups = OneWeekInTheFuture.class) @Max(value = 2022 - 12 - 29, groups = OneWeekInTheFuture.class) private Date planDate; @Column private String weekday; @ManyToOne @JoinColumn(name = "planId", nullable = false) private Plans plans; @ManyToOne @JoinColumn(name = "medsId", nullable = false) private Meds meds; @Column private int medsToTake; }
控制器代码
@PostMapping(value = "/plan/save") public String savePlans(@RequestBody List<PlanDay> plans) { return saveService.savePlans(plans); }
测试建议
一、单元测试(Mockito 模拟依赖)
单元测试聚焦业务逻辑,无需真实数据库,用Mockito模拟medsRepo和planDayRepo:
1. 正常场景:单个PlanDay,药品数量充足
- 步骤:
- 构造
PlanDay对象,关联一个Meds(比如medsId=1,pillNumber=10),设置medsToTake=3 - 模拟
medsRepo.getReferenceById(1)返回该Meds - 模拟
medsRepo.findById(1)返回该Meds - 调用
savePlans方法
- 构造
- 验证:
- 返回值为
"Saved Successfully" Meds的pillNumber被更新为7planDayRepo.save()被调用1次medsRepo的相关方法调用次数符合预期
- 返回值为
2. 正常场景:多个PlanDay,药品均充足
- 构造2个
PlanDay,分别关联不同或相同的Meds(比如一个关联pillNumber=10的药品,取3片;另一个关联pillNumber=20的药品,取5片) - 模拟对应
medsRepo的方法返回 - 验证:
- 返回成功信息
- 所有关联的
Meds的pillNumber都正确更新 - 每个
PlanDay都被保存
3. 异常场景:药品数量不足
- 构造
PlanDay,关联的Meds的pillNumber=2,medsToTake=3 - 模拟
medsRepo的方法返回该Meds - 验证:
- 返回错误信息
"Error: No pills available (Existing Pills: 2)" planDayRepo.save()未被调用Meds的pillNumber未被修改
- 返回错误信息
4. 异常场景:药品不存在
- 构造
PlanDay关联不存在的medsId=999 - 模拟
medsRepo.getReferenceById(999)抛出EntityNotFoundException,或者medsRepo.findById(999)返回Optional.empty()(此时调用get()会抛出NoSuchElementException) - 验证方法是否正确处理该异常(如果代码未加捕获,需测试异常抛出情况,或考虑在业务代码中增加异常处理)
二、集成测试(真实数据库验证)
集成测试验证完整流程,用嵌入式数据库(如H2)模拟真实环境:
1. 准备测试数据
- 测试前插入测试用的
Meds和Plans数据,比如插入Meds(medsId=1, pillNumber=10)、Plans(planId=1)
2. 测试正常保存
- 构造
List<PlanDay>,包含一个关联medsId=1和planId=1的PlanDay,medsToTake=3 - 调用控制器的
/plan/save接口,或直接调用saveService.savePlans() - 验证:
- 返回成功信息
- 查询数据库,
Meds的pillNumber变为7 PlanDay表中存在这条记录
3. 测试中途失败的情况
- 构造2个
PlanDay:第一个关联pillNumber=10的药品,取3片;第二个关联pillNumber=2的药品,取3片 - 调用方法后,验证返回错误信息
- 注意:当前代码无事务管理,第一个
PlanDay会被保存,Meds的数量也会被更新,这是潜在问题。测试时可验证该行为,后续可考虑给方法添加@Transactional注解保证原子性。
三、删除服务的测试思路
对于类似的、通过循环更新pillNumber的删除服务,测试逻辑和savePlans一致:
- 正常场景:删除单个/多个
PlanDay,验证对应的Meds的pillNumber正确增加(比如删除一个取了3片的记录,药品数量加3),且PlanDay被从数据库中移除 - 异常场景:删除不存在的
PlanDay,验证方法是否正确处理(比如无操作、返回提示) - 批量删除:删除多个关联不同药品的
PlanDay,验证所有药品的数量都正确更新
内容的提问来源于stack exchange,提问作者worker24_7
相关产品推荐
相关产品推荐

