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()
方法,但仍然需要提供
高度
值来断言等。但这又有什么错呢?您正在更改构造函数,所以很明显测试会更改。如果我在
设置()方法中进行测试,我只需更改它。但是,我找不到一种方法来参数化
设置()
方法。这很明显-参数化正在“当前测试运行”中进行上下文-您将如何参数化普通方法?使用参数提供程序是个好主意,它启发了我使用包装类。但是假设我想实现参数提供程序-您能详细说明它将如何将对象注入到我的设置代码中吗?