Repository的JUnit测试写法是否合规?Mockito报NullPointerException
问题分析与解决方案
首先,你的测试代码出现NullPointerException的核心原因是混淆了单元测试与集成测试的逻辑,并且对Mockito的使用方式有误。咱们一步步拆解问题:
为什么会抛出NPE?
你用@Autowired从Spring上下文注入了roomRepository,但又试图用Mockito对这个真实的Spring Bean执行when()操作——这完全矛盾:
- 如果Spring上下文加载失败(比如配置文件路径错误、缺少数据库连接依赖),
roomRepository会是null,调用Mockito.when()自然会触发NPE; - 即使
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
相关产品推荐
相关产品推荐

