You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JUnit5中如何替换父类抽象测试的@CsvFileSource实现子类多数据源消重

JUnit 5 消除重复测试代码解决方案

完全可以实现这类重复代码的消除,最简洁易维护的方案是通过抽象基类封装所有公共测试逻辑,结合JUnit 5的ExtensionContext动态获取测试类信息生成对应配置,具体实现如下:

步骤1:编写公共抽象测试基类

把所有重复的测试逻辑、参数加载逻辑都收拢到抽象基类中:

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;

abstract class PropertyBaseTest {
    private Property underTest;

    /**
     * 子类实现,返回对应Property接口的实现类实例
     */
    protected abstract Property getPropertyInstance();

    /**
     * 动态生成CSV资源路径,默认按测试类名规则生成,有特殊需求的子类可重写该方法自定义
     */
    protected String getCsvResourcePath() {
        String propertyName = this.getClass().getSimpleName()
                .replace("Test", "")
                .toLowerCase();
        return "/property/" + propertyName + ".csv";
    }

    @BeforeEach
    void setUp() {
        underTest = getPropertyInstance();
    }

    @DisplayName("given test() method")
    @ParameterizedTest(name = "when number is \"{0}\" then \"{1}\"")
    @MethodSource("loadTestCsvData")
    void testProperty(BigInteger number, boolean expected) {
        assertEquals(expected, underTest.test(number));
    }

    /**
     * 动态加载对应CSV文件的测试数据
     */
    static Stream<Arguments> loadTestCsvData(ExtensionContext context) throws Exception {
        PropertyBaseTest testInstance = (PropertyBaseTest) context.getRequiredTestInstance();
        String csvPath = testInstance.getCsvResourcePath();
        // 跳过第一行表头,解析CSV内容,格式复杂建议引入commons-csv库解析
        return Files.lines(Paths.get(PropertyBaseTest.class.getResource(csvPath).toURI()))
                .skip(1)
                .map(line -> line.split(","))
                .map(arr -> Arguments.of(
                        new BigInteger(arr[0].trim()),
                        Boolean.parseBoolean(arr[1].trim())
                ));
    }
}

步骤2:编写各属性对应的测试子类

子类仅需实现返回对应属性实例的方法,不需要重复写任何测试逻辑:

// Jumping属性测试类
@DisplayName("Given Jumping property")
class JumpingTest extends PropertyBaseTest {
    @Override
    protected Property getPropertyInstance() {
        return new Jumping();
    }
}
// Buzz属性测试类
@DisplayName("Given Buzz property")
class BuzzTest extends PropertyBaseTest {
    @Override
    protected Property getPropertyInstance() {
        return new Buzz();
    }
}

方案优势

  • 零重复代码:所有公共逻辑全部收拢到基类,后续新增属性测试仅需写10行以内的子类代码即可
  • 灵活性高:默认按规则自动生成CSV路径,特殊场景下子类可重写getCsvResourcePath自定义路径
  • 兼容原有逻辑:执行效果和之前的单类测试完全一致,不需要调整已有的CSV测试用例文件
  • 兼容性好:支持JUnit 5.0及以上所有版本,不需要依赖高阶特性

内容的提问来源于stack exchange,提问作者Jegors Čemisovs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 09:45:04