在 Spring 集成测试期间刷新/重建特定的 bean

Cra*_*tis 7 java junit spring integration-testing spring-boot

我们现有的 Spring Boot 集成设置用于@DirtiesContext在不同的测试方法之间重建整个 bean 池。

这是相当慢的,因此我们开始使用可以“刷新”或在内部拆除/重建的 bean ,而无需重新创建实例。

问题是只有一些bean 支持这一点。如果我们控制UsersBean,我们可以实现一个UsersBean.refresh()方法并在我们的方法中调用它@After

但是,如果我们现有的 bean/类不支持刷新,或者我们无法控制,那么我们如何有条件地指示某些 bean 在特定测试后需要被弄脏/重建?

或者更简洁地说:有没有办法在测试方法结束时将 Bean 池的一部分标记为脏,以便重建?

df7*_*899 4

看起来这是可能的,至少在 Spring Boot 环境中是这样。那里的实现ApplicationContext有一个GenericApplicationContext,它能够删除BeanDefinition() ,然后可以通过registerBeanDefinition()重新注册。

这甚至可以级联删除包含对要删除的 bean 的引用的 bean(其实现可以在DefaultSingletonBeanRegistry.destroyBean()中看到)。

例如,如果Bean1被引用Bean2

@Component
public class Bean1 {

}

@Component
public class Bean2 {
    @Autowired
    public Bean1 bean1;
}
Run Code Online (Sandbox Code Playgroud)

然后测试可以bean1从上下文中删除,并查看bean2替换:

@RunWith(SpringRunner.class)
public class BeanRemovalTest implements ApplicationContextAware {
    @Autowired
    private Bean1 bean1;

    @Autowired
    private Bean2 bean2;

    private ApplicationContext applicationContext;

    @Test
    public void test1() throws Exception {
        System.out.println("test1():");
        System.out.println("  bean1=" + bean1);
        System.out.println("  bean2.bean1=" + bean2.bean1);

        resetBean("bean1");
    }

    @Test
    public void test2() throws Exception {
        System.out.println("test2():");
        System.out.println("  bean1=" + bean1);
        System.out.println("  bean2.bean1=" + bean2.bean1);
    }

    private void resetBean(String beanName) {
        GenericApplicationContext genericApplicationContext = (GenericApplicationContext) applicationContext;
        BeanDefinition bd = genericApplicationContext
                .getBeanDefinition(beanName);
        genericApplicationContext.removeBeanDefinition("bean1");
        genericApplicationContext.registerBeanDefinition("bean1", bd);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext = applicationContext;
    }
}
Run Code Online (Sandbox Code Playgroud)

这显示两个 bean 实例都被替换:

test1():
  bean1=hello.so.Bean1@61d6015a
  bean2.bean1=hello.so.Bean1@61d6015a

test2():
  bean1=hello.so.Bean1@2e570ded
  bean2.bean1=hello.so.Bean1@2e570ded
Run Code Online (Sandbox Code Playgroud)

(如果被resetBean("bean1")注释掉,则两次都是同一个实例)。

肯定存在无法解决问题的边缘 - 例如,如果另一个 bean 持有从 获得的引用ApplicationContext.getBean()