java - Spring bean 范围为 "one object per test method"

标签 java spring spring-boot dependency-injection spring-test

我有一个测试实用程序,我需要每个测试方法有一个新的实例(以防止测试之间的状态泄漏)。到目前为止,我使用的是范围“原型(prototype)”,但现在我希望能够将该实用程序连接到另一个测试实用程序,并且每个测试的连接实例应相同。

这似乎是一个标准问题,所以我想知道是否有“测试方法”范围或类似的东西?

这是测试类和测试实用程序的结构:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTest {

    @Autowired
    private TestDriver driver;

    @Autowired
    private TestStateProvider state;

    // ... state
    // ... methods
}
@Component
@Scope("prototype") // not right because MyTest and TestStateProvider get separate instances
public class TestDriver {
    // ...
}
@Component
public class TestStateProvider {

    @Autowired
    private TestDriver driver;

    // ...
}

我知道我可以使用 @Scope("singleton")@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) 但这刷新超出了我的需要 -每个测试一个新的 TestDriver 实例就足够了。此外,这种方法很容易出错,因为使用 TestDriver 的所有测试都需要知道它们还需要 @DirtiesContext 注释。所以我正在寻找更好的解决方案。

最佳答案

实现 testMethod 范围实际上非常容易:

public class TestMethodScope implements Scope {
    public static final String NAME = "testMethod";

    private Map<String, Object> scopedObjects = new HashMap<>();
    private Map<String, Runnable> destructionCallbacks = new HashMap<>();

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        if (!scopedObjects.containsKey(name)) {
            scopedObjects.put(name, objectFactory.getObject());
        }
        return scopedObjects.get(name);
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
        destructionCallbacks.put(name, callback);
    }

    @Override
    public Object remove(String name) {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getConversationId() {
        return null;
    }

    @Override
    public Object resolveContextualObject(String key) {
        return null;
    }

    public static class TestExecutionListener implements org.springframework.test.context.TestExecutionListener {

        @Override
        public void afterTestMethod(TestContext testContext) throws Exception {
            ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) testContext
                    .getApplicationContext();
            TestMethodScope scope = (TestMethodScope) applicationContext.getBeanFactory().getRegisteredScope(NAME);

            scope.destructionCallbacks.values().forEach(callback -> callback.run());

            scope.destructionCallbacks.clear();
            scope.scopedObjects.clear();
        }
    }

    @Component
    public static class ScopeRegistration implements BeanFactoryPostProcessor {

        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
            factory.registerScope(NAME, new TestMethodScope());
        }
    }

}

只需注册测试执行监听器,所有 @Scope("testMethod") 注解类型的每个测试都会有一个实例:

@RunWith(SpringRunner.class)
@SpringBootTest
@TestExecutionListeners(listeners = TestMethodScope.TestExecutionListener.class, 
        mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
public class MyTest {

    @Autowired
    // ... types annotated with @Scope("testMethod")

}

关于java - Spring bean 范围为 "one object per test method",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56342167/

相关文章:

java - 绘制控制流图

java - 数组引用表达式未完全求值

java - 如何用hibernate获取表中的所有数据?

spring - <context :include-filter> and <context:exclude-filter> work in Spring? 怎么办

spring - spring事务管理和hibernate事务管理的区别

java - spring boot 组件扫描包含单个类

spring-boot - Spring Boot YML Config类继承

java - Android 复选框 Listview 回收导致意外的框检查

java - 如何将现有的 Java keystore (.jks) 文件导入 Java 安装?

java - 部署到 Tomcat 服务器时包含 spring-boot-starter-tomcat 会出现什么问题?