你好Java测试人员。 几天前我开始使用JUnit 5,因为我喜欢创建参数化测试的新方法。
@ParameterizedTest
允许测试以参数化方式运行,@CsvFileSource
允许从CSV文件加载参数。
问题是我的CSV中有太多列,并且不希望在我的单元测试中有一个巨大的方法签名。让我举个例子:
@ParameterizedTest
@CsvFileSource(resources = "/test-data.csv")
void myTest(String p1, String p2, String p3, String p4, String p5, String p6) {
//test using parameters
}
我想知道是否有某种转换器可以为我做这样的事情:
@ParameterizedTest
@CsvFileSource(resources = "/test-data.csv")
void myTest(@ConvertWith(TestDataConverter.class) TestData testData) {
//test using parameters
}
static class TestDataConverter implements TestConverter{
public TestData convert(Object ... params){
//a simple method that creates the TestData object and inserts the params in it
}
}
就是这样。
答案 0 :(得分:3)
更新了答案
从JUnit Jupiter 5.2开始:是,专门用于ArgumentsAggregator
和ArgumentsAccessor
API。
查看JUnit用户指南中的PersonAggregator
示例,了解具体示例。
原始答案
从JUnit Jupiter 5.0开始:不,目前还没有可以为您做到这一点的开箱即用转换器。
原因:JUnit Jupiter中的参数化测试支持不支持从多个参数到单个参数的映射。所以,正如@Sormuras建议的那样,你可以打开一个问题来推荐它。
在执行实际转换方面,您可以考虑使用{Jntit Jupiter内部用于解析CSV文件的uniVocity-parsers。 uniVocity-parsers还支持直接从CSV文件映射到 beans ,但要使用它,您需要实现自己的@TestTemplate
读取CSV文件并执行映射。 / p>
其他选项包括来自Jackson framework或来自JSefa project。
的CSV支持答案 1 :(得分:2)
不是一般解决方案,但有时可以使用的一个小技巧:
sed
,awk
或任何类似的工具是您的朋友)现在实现TestConverter,以便将单个字符串转换为String [],以便根据需要使用。在许多情况下,它可能很简单:
static class TestDataConverter implements TestConverter{
public TestData convert(String string){
String[] params = string.split(",");
//a simple method that creates the TestData object and inserts the params in it
}
}
一个非常常见的情况是不允许直接使用该技巧,因为CSV中已经使用引号来处理字段中的“逗号”:
Name, complex field, City
Carlo, "this field could contain , and other critical chars" , London
Mario, "this field could contain , and other critical chars" , New York
Luca, "this field could contain , and other critical chars" , Milan
使用相同引号的引号行显然不起作用。
然而,可以通过CSV文件的一些额外(但无聊)转换来解决这种或类似的情况。
例如,假设3个连续的锐利“###”可以被视为一致的分隔符:
"Name###complex field###City"
"Carlo###this field could contain , and other critical chars###London"
"Mario###this field could contain , and other critical chars###New York"
"Luca###this field could contain , and other critical chars###Milan"
我知道......这是一个技巧..不是一个明确而优雅的解决方案...... :-D
显然在这种情况下,分裂将在3个哈希上:
String[] params = string.split("###");
答案 2 :(得分:0)
您可以使用此聚合器,尽管有点黑:
public class CsvToMapAggregator implements ArgumentsAggregator {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@AggregateWith(CsvToMapAggregator.class)
public @interface CsvToMap {}
private static String[] keys = null; // ATTN: Do not use in parallel runs !!!
@Override public Map<String, Object> aggregateArguments(ArgumentsAccessor accessor, ParameterContext context) throws ArgumentsAggregationException {
Object[] values = accessor.toArray();
if (keys == null) {
keys = (String[]) values; // First Line: Header
} else if (values.length != keys.length) {
keys = null; // Last Line: End of Data
return null;
}
Map<String, Object> map = new HashMap<>();
for (int i = 0; i < values.length; i++) {
map.put(keys[i], values[i]); // Read Data from Line
}
return map;
}
}
注意: 所有行的项目数必须相同。
csv文件必须以另一行结尾(例如,第一列中的“ EOF”),该行数与前几行不同。
测试方法如下所示,以跳过标题和结束行:
@ParameterizedTest(name = "{0}")
@CsvFileSource(resources = "/testcases.csv", delimiter = ';')
@DisplayName("read complete .csv and map columns")
void testCsvData(@CsvToMap Map<String, Object> map, TestInfo info) {
switch (info.getDisplayName()) {
case "Testcase": // First Line: Header
case "EOF": // Last Line: End of File
break;
default:
assertNotNull(map);
assertFalse(map.isEmpty());
}