JUnit测试cancelOrder抛出NullPointerException问题排查
问题根因
问题由测试代码编写疏漏+业务代码缺少鲁棒性校验共同导致,其中测试代码问题是触发本次空指针的直接原因。
测试代码问题
- Mock注解未初始化:你使用
@Mock标注了OrderDao,但没有启用Mockito注解处理,导致注入到OrderServiceImpl里的orderDao实际是null,调用orderDao.getById()时直接触发空指针。修复方式二选一:- 在测试类上添加JUnit5的Mockito扩展注解:
@ExtendWith(MockitoExtension.class) - 在
setUp()方法第一行添加MockitoAnnotations.openMocks(this);手动初始化Mock对象
- 在测试类上添加JUnit5的Mockito扩展注解:
- Mock规则参数不匹配:你写的
Mockito.when(orderDao.save(expectedResult)).thenReturn(expectedResult)要求入参必须是你提前new的expectedResult对象才会触发返回逻辑,但实际业务中传入save()的是修改了statusId后的mockOrder实例,和你new的expectedResult不是同一个对象,该Mock规则不会生效,save方法会返回Mock默认值null。 - 断言逻辑不合理:业务逻辑中
cancelOrder是修改查询到的原有订单对象的状态,不会生成新的Order实例,你提前new的expectedResult和实际方法返回的对象不是同一个实例,如果Order类没有重写equals()方法,哪怕字段值完全一致,assertEquals也会断言失败。
修正后的测试代码示例
@ExtendWith(MockitoExtension.class) class OrderServiceImplTest { OrderServiceImpl orderService; @Mock OrderDao orderDao; @BeforeEach void setUp() { orderService = new OrderServiceImpl(orderDao); } @Test void cancelOrder() { Integer id = 1; // 初始状态为1,符合可取消条件 Order mockOrder = new Order(1, 1, null, null, 1, null, 1); Mockito.when(orderDao.getById(id)).thenReturn(mockOrder); // 匹配任意Order类型入参,调用save时返回传入的参数本身 Mockito.when(orderDao.save(Mockito.any(Order.class))).thenAnswer(invocation -> invocation.getArgument(0)); Order actualResult = orderService.cancelOrder(id); // 断言返回订单状态已修改为5 assertEquals(5, actualResult.getStatusId()); // 断言DAO层的save方法确实被调用,且入参状态为5 Mockito.verify(orderDao).save(Mockito.argThat(order -> order.getStatusId() == 5)); } }
业务代码可优化点
cancelOrder方法缺少空值校验,当传入不存在的订单ID时,orderDao.getById(id)会返回null,后续调用orderToCancel.getStatusId()会直接抛出空指针,建议补充非空判断:
@Override public Order cancelOrder(Integer id) { Order orderToCancel = this.orderDao.getById(id); // 补充订单不存在的校验 if (orderToCancel == null) { // 可根据业务需求抛出业务异常或返回null return null; } if(orderToCancel.getStatusId() != 3 && orderToCancel.getStatusId() != 4){ orderToCancel.setStatusId(5); return this.orderDao.save(orderToCancel); } return null; }
内容的提问来源于stack exchange,提问作者mikal24
相关产品推荐
相关产品推荐

