“每个测试方法一个对象”的 Spring bean 范围

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


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


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


@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注释。所以我正在寻找更好的解决方案。


明月笑刀无情
浏览 130回答 2
2回答

四季花海

testMethod实现作用域实际上很容易:public class TestMethodScope implements Scope {&nbsp; &nbsp; public static final String NAME = "testMethod";&nbsp; &nbsp; private Map<String, Object> scopedObjects = new HashMap<>();&nbsp; &nbsp; private Map<String, Runnable> destructionCallbacks = new HashMap<>();&nbsp; &nbsp; @Override&nbsp; &nbsp; public Object get(String name, ObjectFactory<?> objectFactory) {&nbsp; &nbsp; &nbsp; &nbsp; if (!scopedObjects.containsKey(name)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; scopedObjects.put(name, objectFactory.getObject());&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return scopedObjects.get(name);&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public void registerDestructionCallback(String name, Runnable callback) {&nbsp; &nbsp; &nbsp; &nbsp; destructionCallbacks.put(name, callback);&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public Object remove(String name) {&nbsp; &nbsp; &nbsp; &nbsp; throw new UnsupportedOperationException();&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public String getConversationId() {&nbsp; &nbsp; &nbsp; &nbsp; return null;&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public Object resolveContextualObject(String key) {&nbsp; &nbsp; &nbsp; &nbsp; return null;&nbsp; &nbsp; }&nbsp; &nbsp; public static class TestExecutionListener implements org.springframework.test.context.TestExecutionListener {&nbsp; &nbsp; &nbsp; &nbsp; @Override&nbsp; &nbsp; &nbsp; &nbsp; public void afterTestMethod(TestContext testContext) throws Exception {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) testContext&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .getApplicationContext();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; TestMethodScope scope = (TestMethodScope) applicationContext.getBeanFactory().getRegisteredScope(NAME);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; scope.destructionCallbacks.values().forEach(callback -> callback.run());&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; scope.destructionCallbacks.clear();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; scope.scopedObjects.clear();&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; @Component&nbsp; &nbsp; public static class ScopeRegistration implements BeanFactoryPostProcessor {&nbsp; &nbsp; &nbsp; &nbsp; @Override&nbsp; &nbsp; &nbsp; &nbsp; public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; factory.registerScope(NAME, new TestMethodScope());&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}@Scope("testMethod")只需注册测试执行侦听器,所有注释类型的每个测试都会有一个实例:@RunWith(SpringRunner.class)@SpringBootTest@TestExecutionListeners(listeners = TestMethodScope.TestExecutionListener.class,&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; mergeMode = MergeMode.MERGE_WITH_DEFAULTS)public class MyTest {&nbsp; &nbsp; @Autowired&nbsp; &nbsp; // ... types annotated with @Scope("testMethod")}

慕容森

我前段时间遇到了同样的问题并得出了这个解决方案:使用模拟我编写了一些方法来创建特定的 mockito 设置以向每个 mock 添加行为。因此,使用以下方法和 bean 定义创建一个 TestConfiguration 类。&nbsp; &nbsp; private MockSettings createResetAfterMockSettings() {&nbsp; &nbsp; &nbsp; &nbsp; return MockReset.withSettings(MockReset.AFTER);&nbsp; &nbsp; }&nbsp; &nbsp; private <T> T mockClass(Class<T> classToMock) {&nbsp; &nbsp; &nbsp; &nbsp; return mock(classToMock, createResetAfterMockSettings());&nbsp; &nbsp; }您的 bean 定义将如下所示:@Beanpublic TestDriver testDriver() {&nbsp; &nbsp; return mockClass(TestDriver .class);}MockReset.AFTER用于在运行测试方法后重置模拟。最后添加一个TestExecutionListeners到你的测试类:@TestExecutionListeners({ResetMocksTestExecutionListener.class})
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java