Spring MVC+JUnit测试:Repository调用时出现NullPointerException
解决JUnit测试Spring MVC Repository时的NullPointerException问题
咱们先捋清楚你遇到的问题:给Spring MVC项目里的Repository写JUnit测试时,调用findByLoginInformations方法触发了NullPointerException,错误指向实现类的第77行,也就是操作sessionFactory的代码块。先把你的相关代码整理清楚:
相关代码
PersonneRepository接口
public interface PersonneRepository { void setSessionFactory(SessionFactory sessionFactory); Personne findByLoginInformations(String email, String password); }
PersonneRepositoryImpl实现类
@Repository public class PersonneRepositoryImpl implements PersonneRepository { private SessionFactory sessionFactory; @Autowired public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public Personne findByLoginInformations(String email, String password){ Session session; try { session = sessionFactory.getCurrentSession(); } catch (HibernateException e) { session = sessionFactory.openSession(); } Query query = session .createQuery("FROM Personne p WHERE p.email = :email AND p.password = :password") .setParameter("email", email) .setParameter("password", password); return (Personne)query.uniqueResult(); } }
原测试类
public class PersonneRepositoryTest { @Mock private SessionFactory sessionFactory; @InjectMocks private PersonneRepositoryImpl personneRepositoryImpl; @Before public void setUp() { MockitoAnnotations.initMocks(this); } @Test public void testFindPersonne(){ Personne p = new Personne(); p.setId(1); when(personneRepositoryImpl.findByLoginInformations("ayoub", "aaaa")).thenReturn(p); assertEquals(p, personneRepositoryImpl.findById(1)); verify(personneRepositoryImpl).findById(1); } }
问题排查
咱们拆解下核心问题:
- 测试逻辑完全走偏了:你现在是在mock
personneRepositoryImpl自己的findByLoginInformations方法,然后去调用一个根本不存在的findById方法(你的Repository接口里没有这个方法,明显是笔误),这完全没测试到你写的实现类真实逻辑。 - NPE的直接原因:虽然你用
@Mock创建了sessionFactory的代理对象,但Mockito默认会给所有未指定返回值的方法返回null。当你调用findByLoginInformations时,实际执行的是实现类的代码,sessionFactory.getCurrentSession()返回null,catch块里的sessionFactory.openSession()也返回null,导致session变量为null,后续调用session.createQuery()就触发了NPE。
修复方案
咱们需要修正测试逻辑,正确mock依赖对象的行为,来测试实现类的真实逻辑:
修正后的测试类
public class PersonneRepositoryTest { // Mock所有需要的依赖对象 @Mock private SessionFactory sessionFactory; @Mock private Session session; @Mock private Query query; // 注入要测试的实现类 @InjectMocks private PersonneRepositoryImpl personneRepositoryImpl; @Before public void setUp() { MockitoAnnotations.initMocks(this); } @Test public void testFindByLoginInformations() { // 1. 准备预期的返回对象 Personne expectedPersonne = new Personne(); expectedPersonne.setId(1); expectedPersonne.setEmail("ayoub"); expectedPersonne.setPassword("aaaa"); // 2. 模拟依赖对象的行为 // 让sessionFactory.getCurrentSession()返回mock的session when(sessionFactory.getCurrentSession()).thenReturn(session); // 让session创建query时返回mock的query when(session.createQuery("FROM Personne p WHERE p.email = :email AND p.password = :password")) .thenReturn(query); // 模拟链式调用的setParameter方法,返回query自身 when(query.setParameter("email", "ayoub")).thenReturn(query); when(query.setParameter("password", "aaaa")).thenReturn(query); // 让query返回预期的Personne对象 when(query.uniqueResult()).thenReturn(expectedPersonne); // 3. 调用要测试的方法 Personne actualPersonne = personneRepositoryImpl.findByLoginInformations("ayoub", "aaaa"); // 4. 断言结果是否符合预期 assertEquals(expectedPersonne, actualPersonne); // 5. 验证依赖方法是否被正确调用 verify(sessionFactory).getCurrentSession(); verify(session).createQuery(anyString()); verify(query, times(2)).setParameter(anyString(), anyString()); verify(query).uniqueResult(); } }
额外注意事项
- 删掉测试里的
findById相关代码,那是笔误,咱们的测试目标是findByLoginInformations方法。 - 不要mock你要测试的类本身(也就是
personneRepositoryImpl),而是mock它的依赖(sessionFactory、Session、Query),这样才能测试你自己写的业务逻辑。 - 如果要测试
catch块的逻辑(比如getCurrentSession抛出异常的情况),可以用when(sessionFactory.getCurrentSession()).thenThrow(new HibernateException("test exception"))来模拟异常场景,验证是否会调用openSession。
内容的提问来源于stack exchange,提问作者user7179261
相关产品推荐
相关产品推荐

