JUnit 5 - 将多个CSV文件参数转换为单个对象

时间:2017-11-29 12:12:03

标签: java csv parameters junit5

你好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
    }
}

就是这样。

3 个答案:

答案 0 :(得分:3)

更新了答案

从JUnit Jupiter 5.2开始:,专门用于ArgumentsAggregatorArgumentsAccessor 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)

不是一般解决方案,但有时可以使用的一个小技巧:

  • 将CSV文件的每一行括在引号之间...以便每行只被视为一个字符串(sedawk或任何类似的工具是您的朋友)
  • 现在实现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());
}