您可以编写自己的参数提供程序,它提供一个完全构造的世界对象并将其注入到您的 setup/BeforeEach 方法中(您将其存储在您的测试方法中使用)。这样您就可以最小化您的设置代码并将您的初始化逻辑浓缩到代码中的一个特定位置。
编辑:我想到了一个错误的机制。所以这里是正确的例子,它澄清了我的意思:
让我们假设你的世界对象看起来像这样:
class World {
private final int width;
private final int height;
public World(int width, int height) {
this.width = width;
this.height = height;
}
@Override
public String toString() {
return "World{" + "width=" + width + ", height=" + height + '}';
}
}
您有几个测试方法,您需要一组不同的 World 对象(可能是动态创建的),如下所示:
class ArgumentProviderTest {
@ParameterizedTest
@ArgumentsSource(WorldArgumentProvider.class)
void yourFirstTest(World w) {
System.out.println(w);
}
@ParameterizedTest
@ArgumentsSource(WorldArgumentProvider.class)
void yourSecondTest(World w) {
System.out.println(w);
}
}
你看,测试都期望一个 World 对象作为参数。现在我们需要一种方法来提供世界对象。为此,我们编写了自己的 ArgumentsProvider,它也很简单:
class WorldArgumentProvider implements ArgumentsProvider {
private Collection<World> createWorlds() {
// Somehow create a bunch of World objects
// !!! Here goes your initialization logic. !!!
return ThreadLocalRandom.current()
.ints()
.limit(10)
.mapToObj(i -> new World(i % 100, i / 100))
.collect(Collectors.toList());
}
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
// create an Arguments object from these (discrete) worlds.
// remember: one method takes one Arguments object which carries each single argument/parameter
return createWorlds().stream().map(Arguments::of);
}
}
在这里,您可以使用完整的编译器支持做任何您想做的事情(而不是使用方法名称作为字符串)。
也许您可以使用带有 @TestFactory 的 DynamicTests 实现类似的行为,但这取决于您的确切测试。
我的第一个想法是使用 ParameterResolver,它的工作原理基本上是这样的:
- 将 ParameterResolver 注册为扩展(在此上下文中,在类级别而不是方法级别)
- 为您的测试和生命周期方法 (BeforeEach/AfterAll/...) 的每个(未解决的)参数调用此解析器
- 它决定它是否真的可以解析这个参数(例如,正确的类型、正确的索引等)
- 它提供实际的 Argument 作为参数
但是,通过这种机制,你不能告诉 Jupiter 引擎为一个方法构造多个 Test 实例(Jupiter 为这样的事情引入了 TestTemplate 的概念)。有趣的是,这个 Parameterized-Class conect 是在 jUnit-4 中实现的(作为参数化测试的唯一方法),并且是 AFAIK 有望在 Jupiter 未来出现的功能。我很期待。