You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Repository的JUnit测试写法是否合规?Mockito报NullPointerException

问题分析与解决方案

首先,你的测试代码出现NullPointerException的核心原因是混淆了单元测试与集成测试的逻辑,并且对Mockito的使用方式有误。咱们一步步拆解问题:

为什么会抛出NPE?

你用@Autowired从Spring上下文注入了roomRepository,但又试图用Mockito对这个真实的Spring Bean执行when()操作——这完全矛盾:

  1. 如果Spring上下文加载失败(比如配置文件路径错误、缺少数据库连接依赖),roomRepository会是null,调用Mockito.when()自然会触发NPE;
  2. 即使roomRepository成功注入,它是真实的Repository实例,并非Mockito创建的Mock对象,Mockito无法对非Mock对象进行方法 Stub 操作。

另外,你的代码还有两个容易被忽略的小问题:

  • SimpleDateFormat的格式错误:dd-mm-yyyy里的mm代表分钟,月份应该用MM,否则日期解析会完全错误;
  • 测试方法名拼写错误:findRommByDateTest多写了一个m,虽然不影响运行,但建议修正为findRoomByDateTest。

两种正确的测试方案

方案1:单元测试(Mock Repository,测试依赖它的上层逻辑)

如果你是想测试调用RoomRepository的Service层代码,应该用Mockito Mock掉RoomRepository,不需要加载Spring上下文:

import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

@ExtendWith(MockitoExtension.class)
public class RoomServiceTest {
    // 创建Mock版的RoomRepository
    @Mock
    private RoomRepository roomRepository;
    
    @Test
    public void findRoomByDateTest() throws ParseException {
        // 注意月份用MM,不是mm!
        Date sDate = new SimpleDateFormat("dd-MM-yyyy").parse("27-07-2020");
        Date eDate = new SimpleDateFormat("dd-MM-yyyy").parse("28-07-2020");
        String roomType = "SINGLE";
        
        // 准备模拟返回数据
        RoomEntity mockRoom = new RoomEntity();
        mockRoom.setRoomId(1);
        mockRoom.setRoomType("SINGLE");
        
        // 对Mock对象设置方法返回规则
        when(roomRepository.findRoomByDate(sDate, eDate, roomType))
            .thenReturn(mockRoom.getRoomId());
        
        // 这里应该调用依赖RoomRepository的Service方法,而非直接调用Mock的方法
        RoomService roomService = new RoomService(roomRepository);
        int resultId = roomService.getAvailableRoomId(sDate, eDate, roomType);
        
        // 验证结果是否符合预期
        assertEquals(1, resultId);
    }
}

方案2:集成测试(测试真实的Repository逻辑)

如果你的测试类RoomRepositoryTest是为了验证Repository层的实际查询逻辑,那应该用Spring加载真实的Repository Bean,连接测试数据库,而不是Mock它:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.junit.jupiter.api.extension.ExtendWith;
import static org.junit.jupiter.api.Assertions.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

@ExtendWith(SpringExtension.class)
// 注意路径:classpath*:用于匹配多个资源,classpath:匹配单个,确保配置文件存在于类路径下
@ContextConfiguration(locations = { "classpath:resources/invoices-context.xml", "classpath:resources/invoices-int-schema.xml" })
public class RoomRepositoryTest {
    @Autowired
    private RoomRepository roomRepository;
    
    // 可以在测试前插入测试数据(需要配合事务回滚避免污染数据库)
    // @BeforeEach
    // @Transactional
    // public void setUpTestData() {
    //     RoomEntity testRoom = new RoomEntity();
    //     testRoom.setRoomId(1);
    //     testRoom.setRoomType("SINGLE");
    //     testRoom.setRoomStatus("AVAILABLE");
    //     roomRepository.save(testRoom);
    // }
    
    @Test
    // 建议加上@Transactional,测试后自动回滚数据
    // @Transactional
    public void findRoomByDateTest() throws ParseException {
        Date sDate = new SimpleDateFormat("dd-MM-yyyy").parse("27-07-2020");
        Date eDate = new SimpleDateFormat("dd-MM-yyyy").parse("28-07-2020");
        String roomType = "SINGLE";
        
        // 调用真实的Repository查询方法
        int resultId = roomRepository.findRoomByDate(sDate, eDate, roomType);
        
        // 验证查询结果是否符合预期
        assertEquals(1, resultId);
    }
}

内容的提问来源于stack exchange,提问作者RS0023

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 16:57:45