在测试运行时部分模拟Spring Bean

3df52oht  于 5个月前  发布在  Spring
关注(0)|答案(1)|浏览(82)

我的Sping Boot v3.2.0应用程序包含一个LocalContainerEntityManagerFactoryBean类型的bean。当测试运行时,我希望此bean的destroy方法(在父类AbstractEntityManagerFactoryBean中定义)不执行任何操作。我尝试使用

import static org.mockito.Mockito.doNothing;
// other imports omitted

@SpringBootTest
public class MyIntegrationTests {

    @SpyBean
    private AbstractEntityManagerFactoryBean entityManagerFactoryBean;

    @Test
    void myIntegrationTest() {
        doNothing().when(entityManagerFactoryBean).destroy();
        // remainder of test omitted     
    }
}

字符串
但是当我运行测试时,我得到了这个错误,它表明没有为entityManagerFactoryBean创建间谍程序

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


即使这个解决方案确实有效,我也不会对它完全满意,因为没有明显的方法可以在其他集成测试类中重用它(除了复制粘贴)。
当测试运行时,是否有其他方法可以存根此bean的destroy方法?

最小可复制示例

通过克隆this GitHub repository并运行./gradlew test可以重现此问题

0yycz8jy

0yycz8jy1#

这与Spring如何示例化EntityManagerFactory有关。你必须使用这样的东西:

@SpyBean(name = "entityManagerFactory", classes = EntityManagerFactory.class)
private Object entityManagerFactoryBean;

字符串
不幸的是,你会发现bean的运行时类型是一个不扩展AbstractEntityManagerFactoryBean的代理(参见AbstractEntityManagerFactoryBean::createEntityManagerFactoryProxy),并且访问代理的delegate字段涉及到打开内部模块,这不是一个好主意。
因此,我建议将现有的EntityManagerFactory Package 在一个代理中,并拦截destroy()方法。为此,要么使用lombok和@Delegate来避免手动编写每个委托方法,要么使用CGLib在类上生成一个动态代理(见下文)。在测试类中添加以下内容可以实现您想要的功能:

@TestConfiguration
static class Config {
    @Bean
    BeanPostProcessor beanPostProcessor() {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof AbstractEntityManagerFactoryBean factoryBean) {
                    return Enhancer.create(bean.getClass(),
                        (InvocationHandler) (proxy, method, args) -> {
                            if (method.getName().equals("destroy")) {
                                System.out.println("not destroying!");
                                return null;
                            } else {
                                return method.invoke(factoryBean, args);
                            }
                        });
                }

                return bean;
            }
        };
    }
}

相关问题