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

Spring Boot 3.2.0测试:如何Stub LocalContainerEntityManagerFactoryBean的destroy方法?

问题描述

我开发的Spring Boot v3.2.0应用中有一个LocalContainerEntityManagerFactoryBean类型的Bean,想在测试运行时让它的父类AbstractEntityManagerFactoryBean的destroy()方法不执行任何操作。

我尝试用以下代码实现:

import static org.mockito.Mockito.doNothing;
// 其他导入省略

@SpringBootTest
public class MyIntegrationTests {

    @SpyBean
    private AbstractEntityManagerFactoryBean entityManagerFactoryBean;

    @Test
    void myIntegrationTest() {
        doNothing().when(entityManagerFactoryBean).destroy();
        // 测试剩余代码省略     
    }
}

但运行测试时出现错误,说明没成功创建Spy:

org.mockito.exceptions.misusing.NotAMockException:
Argument passed to when() is not a mock!

就算这个方案能成,也没法在其他集成测试类里便捷复用(只能复制粘贴),想问有没有其他方法在测试时Stub这个Bean的destroy方法?


可行解决方案

方案1:自定义Bean后置处理器(全局复用)

写一个Bean后置处理器,在Bean初始化完成后用Mockito Spy包装原Bean,同时Stub掉destroy()方法。只需写一次,所有测试类自动生效。

import org.mockito.Mockito;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
import org.springframework.stereotype.Component;

@Component
public class EntityManagerFactoryDestroyStubProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof AbstractEntityManagerFactoryBean) {
            AbstractEntityManagerFactoryBean spyBean = Mockito.spy((AbstractEntityManagerFactoryBean) bean);
            Mockito.doNothing().when(spyBean).destroy();
            return spyBean;
        }
        return bean;
    }
}

将这个类放在src/test/java的对应包下,确保只在测试环境生效,Spring会自动扫描并应用该处理器。

方案2:测试基类(按需复用)

创建一个测试基类,把Spy和Stub逻辑封装进去,其他测试类直接继承即可复用:

import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.junit.jupiter.api.BeforeEach;

@SpringBootTest
public abstract class BaseIntegrationTest {

    @SpyBean
    private LocalContainerEntityManagerFactoryBean entityManagerFactoryBean;

    @BeforeEach
    void setupEntityManagerFactoryStub() {
        // 针对父类的destroy方法做Stub
        Mockito.doNothing().when((AbstractEntityManagerFactoryBean) entityManagerFactoryBean).destroy();
    }
}

测试类继承基类即可:

public class MyIntegrationTests extends BaseIntegrationTest {

    @Test
    void myIntegrationTest() {
        // 测试逻辑正常编写,destroy方法已被Stub
    }
}

注:直接Spy子类LocalContainerEntityManagerFactoryBean可以避免类型匹配问题,再转型为父类Stub目标方法。

方案3:重写Bean(彻底替换)

在测试环境中直接替换原有的LocalContainerEntityManagerFactoryBean,重写destroy()方法为空实现:

import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import org.springframework.orm.jpa.JpaVendorAdapter;

@Configuration
public class TestEntityManagerFactoryConfiguration {

    @Primary
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(
            DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
        LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean() {
            @Override
            public void destroy() {
                // 空实现,不执行任何销毁逻辑
            }
        };
        // 复制原Bean的核心配置,保证测试逻辑正常运行
        bean.setDataSource(dataSource);
        bean.setJpaVendorAdapter(jpaVendorAdapter);
        bean.setPackagesToScan("com.your.app.package");
        return bean;
    }
}

这种方法需要确保复制原Bean的所有必要配置,适合对Bean配置完全可控的场景。


内容的提问来源于stack exchange,提问作者Dónal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 08:57:52