Java 如何测试依赖于初始参数的方法?
在我的测试类中,我有几个参数化方法,其中参数是对象初始化的一部分。我对所有参数都使用相同的参数,然后测试依赖于一个或多个参数的方法Java 如何测试依赖于初始参数的方法?,java,junit,parameters,junit5,Java,Junit,Parameters,Junit5,在我的测试类中,我有几个参数化方法,其中参数是对象初始化的一部分。我对所有参数都使用相同的参数,然后测试依赖于一个或多个参数的方法 @MethodSource(value = "validSizes") @ParameterizedTest void testGetHeight(int terrainWidth, int height) { World world = new World(terrainWidth, height); assertEquals(height, wo
@MethodSource(value = "validSizes")
@ParameterizedTest
void testGetHeight(int terrainWidth, int height) {
World world = new World(terrainWidth, height);
assertEquals(height, world.getHeight());
}
@MethodSource(value = "validSizes")
@ParameterizedTest
void testGetHeight(WorldWrapper param) {
assertEquals(param.height, param.world.getHeight());
}
这可能会导致代码重复-我需要重复参数、World
构造和@MethodSource
注释
我希望避免代码重复,因为如果我向构造函数添加另一个参数,我将不得不更改所有依赖于这些参数的测试方法
@MethodSource(value = "validSizes")
@ParameterizedTest
void testGetHeight(int terrainWidth, int height) {
World world = new World(terrainWidth, height);
assertEquals(height, world.getHeight());
}
@MethodSource(value = "validSizes")
@ParameterizedTest
void testGetHeight(WorldWrapper param) {
assertEquals(param.height, param.world.getHeight());
}
我可以在每次注释之前使用
@before,即参数化setUp()
方法,还是用另一种方法?使用不同的参数测试此类方法是否是一种良好的做法?您可以编写自己的参数提供程序,提供一个完全构造的世界对象,并将其注入到setup/beforeach方法中(您将其存储在测试方法中以供使用)。这样可以最小化设置代码,并将初始化逻辑浓缩到代码中的一个特定位置
编辑:我脑子里想的是一个错误的机制。下面是一个正确的例子,说明了我的意思:
假设您的世界对象如下所示:
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 + '}';
}
}
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);
}
}
您有几种测试方法,其中需要一组不同的世界对象(可能是动态创建的),如下所示:
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 + '}';
}
}
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);
}
}
类WorldArgumentProvider实现ArgumentsProvider{
私人收藏createWorlds(){
//以某种方式创建一组世界对象
//!!!这是您的初始化逻辑!!!
返回ThreadLocalRandom.current()
.ints()
.限额(10)
.mapToObj(i->新世界(i%100,i/100))
.collect(Collectors.toList());
}
@凌驾
publicstream您可以使用包装器类将所有初始化代码放在一个地方
class WorldWrapper {
public final World world;
public final int terrainWidth;
public final int height;
//constructor to initialize the fields
}
您仍然需要使用@MethodSource
注释每个方法,并重复单个参数,但如果您决定更改构造函数参数,则不必担心
@MethodSource(value = "validSizes")
@ParameterizedTest
void testGetHeight(int terrainWidth, int height) {
World world = new World(terrainWidth, height);
assertEquals(height, world.getHeight());
}
@MethodSource(value = "validSizes")
@ParameterizedTest
void testGetHeight(WorldWrapper param) {
assertEquals(param.height, param.world.getHeight());
}
在这种情况下,您无论如何都需要更改所有测试方法,那么有什么区别呢?您可以将World
creation移动到一些getWorld()
方法,但仍然需要提供高度
值来断言等。但这又有什么错呢?您正在更改构造函数,所以很明显测试会更改。如果我在设置()方法中进行测试,我只需更改它。但是,我找不到一种方法来参数化设置()
方法。这很明显-参数化正在“当前测试运行”中进行上下文-您将如何参数化普通方法?使用参数提供程序是个好主意,它启发了我使用包装类。但是假设我想实现参数提供程序-您能详细说明它将如何将对象注入到我的设置代码中吗?