春豆"one object per test method"范围



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

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

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

@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")
}

我前段时间遇到了同样的问题,并得出了这个解决方案:

  1. 使用模拟
  2. 我编写了一些方法来创建特定的模拟设置,以为每个模拟添加行为。

因此,使用以下方法和 Bean 定义创建一个 TestConfiguration 类。

    private MockSettings createResetAfterMockSettings() {
        return MockReset.withSettings(MockReset.AFTER);
    }
    private <T> T mockClass(Class<T> classToMock) {
        return mock(classToMock, createResetAfterMockSettings());
    }

您的 Bean 定义将如下所示:

@Bean
public TestDriver testDriver() {
    return mockClass(TestDriver .class);
}

MockReset.AFTER用于在运行测试方法后重置模拟。

最后向测试类添加一个TestExecutionListeners

@TestExecutionListeners({ResetMocksTestExecutionListener.class})

相关内容

最新更新