在大多数情况下,当我考虑创建扩展时,都会遇到同样的问题:如何存储可变对象?这是我的问题:让我们考虑一下我的扩展提供了一个参数解析器,它为测试提供了一个可变对象。假设该对象具有更改配置的方法。基于JUnit 5 用户指南和Javadoc的简单实现可能如下所示:
public class MyExtension implements ParameterResolver {
private static final Namespace NAMESPACE = Namespace.create(MyExtension.class);
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterContext.getParameter().getType() == MyMutableType.class;
}
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return extensionContext.getStore(NAMESPACE).getOrComputeIfAbsent(MyMutableType.class, __ -> new MyMutableType());
}
}
不幸的是,对于以下测试类,这个实现被破坏了。
@ExtendWith(MyExtension.class)
final class MyTest {
@BeforeAll
static void init(MyMutableType resolvedObject) {
}
@Test
void test1(MyMutableType resolvedObject) {
resolvedObject.changeSomeConfig();
...
}
@Test
void test2(MyMutableType resolvedObject) {
// resolvedObject might be affected by changed configuration in test1().
...
}
}
直到今天,我都找不到好的解决方案。JUnit是否有一个指南应该如何工作。我什么也找不到。为了解决这个问题,我看到了两种方法。它们似乎都不适用于 JUnit 的 API。
ExtensionContext
一种方法是在不特定于单个测试时禁止使用我的参数解析器。不幸的是,我找不到任何可靠的方法来检查它。我可以检查 @BeforeAll 之类的注释,但这更像是一种估计,而不是可靠的检查。
第二种方法是在我们输入更具体的对象时复制对象ExtensionContext
。或者,我可以设置一个标志来防止进一步修改并提供有意义的错误消息。然而,这样的实现远非简单,看起来更像是滥用 API。除此之外,copyOperator
在没有实际必要的情况下使用可能会过于严格。
<T> T getOrCompute(ExtensionContext extensionContext, Object key, Supplier<T> factory, UnaryOperator<T> copyOperator) {
Store store = extensionContext.getStore(NAMESPACE);
// Using remove() because it is the only method ignoring ancestors
Object previous = store.remove(key);
if (previous == null) {
T fromAncestor = (T) store.get(key);
if (fromAncestor == null) {
T result = factory.get();
store.put(key, result);
return result;
}
else {
T result = copyOperator.apply(fromAncestor);
store.put(key, result);
return result;
}
}
else {
store.put(key, previous);
return (T) previous;
}
}
我想知道我是否遗漏了一些重要的东西,或者 JUnit 是否没有有意义的方式来处理扩展中的可变状态。