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

为何Mock的Repository仍尝试连接数据库?——自定义RESTful风格API服务层单元测试问题排查

问题原因分析

我来帮你拆解下这个问题的核心:
你遇到的数据库连接尝试,根本原因是HibernateController里的静态成员初始化逻辑,和Mockito的Mock机制不兼容:

  1. HibernateController中的sessionFactory和session是静态变量,JVM在加载这个类的时候就会执行HibernateUtil.getSessionFactory()和sessionFactory.openSession()——这个过程发生在你创建Mock版ArtikelRepository之前。
  2. Mockito的Mock是基于动态代理实现的,它只能Mock实例方法、拦截实例级别的调用,但完全管不了类加载阶段的静态初始化逻辑。所以哪怕你Mock了ArtikelRepository,父类HibernateController的静态代码已经提前执行,触发了真实的数据库连接流程。
解决建议与替代方案

根据你的代码情况,推荐以下几种解决方案,按优先级排序:

方案一:重构HibernateController,改用依赖注入(最推荐)

这是从根源上解决问题的方式,也更符合面向对象设计原则:
把静态成员改成实例成员,通过构造方法注入SessionFactory,避免类加载时的强制初始化:

// 重构后的HibernateController
public class HibernateController { 
    private final SessionFactory sessionFactory; 
    private Session session; 

    // 通过构造方法注入SessionFactory
    public HibernateController(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
        this.session = sessionFactory.openSession();
    }

    public void deleteById(Object id, Class deleteClass) { 
        session.beginTransaction(); 
        session.delete(session.find(deleteClass, id)); 
        session.getTransaction().commit(); 
    } 
    // ...其他方法
}

// 重构后的ArtikelRepository
public class ArtikelRepository extends HibernateController implements Serializable {
    public ArtikelRepository(SessionFactory sessionFactory) {
        super(sessionFactory);
    }
}

测试时,你可以直接MockSessionFactory传给Repository,或者继续MockArtikelRepository的方法——此时父类的初始化不会触发真实数据库连接,因为依赖的是Mock的SessionFactory。

方案二:用PowerMock拦截静态初始化(无需重构代码)

如果暂时无法重构现有代码,可以用PowerMock来拦截静态方法和类的静态初始化流程:

  1. 首先引入PowerMock依赖(根据你的JUnit版本选择):
    <!-- JUnit 5 + Mockito 2/3 -->
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit5</artifactId>
        <version>2.0.9</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito2</artifactId>
        <version>2.0.9</version>
        <scope>test</scope>
    </dependency>
    
  2. 修改测试类,Mock静态方法和相关依赖:
    @ExtendWith({MockitoExtension.class, PowerMockExtension.class})
    @PrepareForTest({HibernateUtil.class, HibernateController.class})
    class ArtikelServiceUnittest { 
        @Mock
        ArtikelRepository artikelRepository;
        @Mock
        SessionFactory mockSessionFactory;
        @Mock
        Session mockSession;
        @Mock
        Transaction mockTransaction;
    
        ArtikelService cut;
        ArtikelEntity givenArtikelEntity = new ArtikelEntity(1, "Testartikel", "Test", 1, 1.00); 
    
        @BeforeEach
        void setUp() {
            // 拦截HibernateUtil的静态方法,返回Mock的SessionFactory
            PowerMockito.mockStatic(HibernateUtil.class);
            when(HibernateUtil.getSessionFactory()).thenReturn(mockSessionFactory);
            // Mock SessionFactory的openSession方法
            when(mockSessionFactory.openSession()).thenReturn(mockSession);
            // Mock Session的事务方法,避免空指针
            when(mockSession.beginTransaction()).thenReturn(mockTransaction);
    
            cut = new ArtikelService(artikelRepository);
        }
    
        @Test 
        void shouldReturnAListWithTheGivenEntityAsDto() { 
            // given 
            List givenList = new ArrayList() {{ add(givenArtikelEntity.toArtikelDto()); }}; 
            when(artikelRepository.getAll("ArtikelEntity")).thenReturn(givenList); 
            // when 
            List result = cut.getAllArtikel(); 
            // then 
            assertEquals(givenList, result); 
        } 
    }
    

PowerMock会拦截HibernateController的静态初始化流程,让它使用你Mock的SessionFactory,从而避免真实的数据库连接。

方案三:给HibernateUtil添加测试开关(侵入性较小的临时方案)

在HibernateUtil中增加一个测试专用的方法,允许手动设置SessionFactory:

public class HibernateUtil {
    private static SessionFactory sessionFactory;

    public static SessionFactory getSessionFactory() {
        if (sessionFactory == null) {
            // 原有的真实初始化逻辑
        }
        return sessionFactory;
    }

    // 测试专用:手动设置SessionFactory
    public static void setSessionFactoryForTest(SessionFactory factory) {
        sessionFactory = factory;
    }
}

然后在测试类的@BeforeEach中提前设置Mock的SessionFactory:

@BeforeEach
void setUp() {
    SessionFactory mockSessionFactory = Mockito.mock(SessionFactory.class);
    HibernateUtil.setSessionFactoryForTest(mockSessionFactory);
    // 后续Mock Session等逻辑...
    artikelRepository = Mockito.mock(ArtikelRepository.class);
    cut = new ArtikelService(artikelRepository);
}

这样HibernateController初始化时会使用你设置的MockSessionFactory,不会触发真实连接。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 16:27:37