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
相关产品推荐
相关产品推荐

