【问题标题】:How can I return a multidimensional array as a collection of parameters for a JUnit test?如何将多维数组作为 JUnit 测试的参数集合返回?
【发布时间】:2013-12-30 22:33:21
【问题描述】:

我对 Java 还很陌生,而且我肯定是在我的头上。好吧,抛开免责声明,这是我的故事:

我在 XML 文件中有大量视频文件名和互补字符串,称为参考 ID。 XML 如下所示:

<testdata>
  <testcase displayName="video1" refId="vid1xxyyzz" />
  <testcase displayName="video2" refId="vid2aabbcc" />
  .
  .
  <testcase displayName="video499" refId="vid499ffoooo" />
</testdata>

我已经使用 XMLBeans 将 XML 模式转换为一个类,并且可以使用此处记录的方法将数据导入几个数组:http://docs.oracle.com/javase/tutorial/reflect/special/arraySetGet.html

// File import:
    File refIdFile = new File("C:\testdata.xml");
    DisplayNameReferenceIdDoc = DisplayNameReferenceIdDocument.Factory.parse(refIdFile);
    displayNameReferenceId = DisplayNameReferenceIdDoc.getDisplayNameReferenceId();
    tests = displayNameReferenceId.getTestcaseArray();

// Multi-dimensional array get/set:
    matrix = Array.newInstance(String.class, 2, tests.length);
    Object row0 = Array.get(matrix, 0);
    Object row1 = Array.get(matrix, 1);
    for (int i = 0; i < tests.length; i++){
        displayName = tests[i].getDisplayName();
        refId = tests[i].getRefId();
        Array.set(row0, i, displayName);
        Array.set(row1, i, refId);
    }

这一切都发生在我的测试类中的 @Parameterized 方法中,该方法必须返回一个集合。这是整个事情,缩小了:

@RunWith(Parameterized.class)
public class ValidateDisplayNameReferenceIdTest {

static DisplayNameReferenceIdDocument DisplayNameReferenceIdDoc;
static DisplayNameReferenceId displayNameReferenceId;
static DisplayNameReferenceId.Test[] tests;
static String displayName;
static String refId;
static Object matrix;

@Parameterized.Parameters(name="{index}: {0}")
public static Collection<Object> getTestParameters() throws IOException, XmlException {
    File refIdFile = new File("C:\testdata.xml");

    DisplayNameReferenceIdDoc = DisplayNameReferenceIdDocument.Factory.parse(refIdFile);
    displayNameReferenceId = DisplayNameReferenceIdDoc.getDisplayNameReferenceId();
    tests = displayNameReferenceId.getTestArray();
    matrix = Array.newInstance(String.class, 2, tests.length);
    Object row0 = Array.get(matrix, 0);
    Object row1 = Array.get(matrix, 1);
    for (int i = 0; i < tests.length; i++){
        displayName = tests[i].getDisplayName();
        refId = tests[i].getRefId();
        Array.set(row0, i, displayName);
        Array.set(row1, i, refId);
    }
    System.out.println("tweet");
    return Arrays.asList(matrix);  // NOT SURE ABOUT THIS!
}


private String displayNameInput;
private String refIdExpected;

public ValidateDisplayNameReferenceIdTest(String input, String expected ) {
    displayNameInput = input;
    refIdExpected = expected;
}

@Test
public void test() throws IOException {
    // send the API URL with 'displayNameInput', validate result for 'refIdExpected'
    URLConnection connection = new URL(url1 + displayNameInput + url2 + token).openConnection();
    connection.setRequestProperty("Accept-Charset", charset);
    InputStream response = connection.getInputStream();
    StringWriter writer = new StringWriter();
    IOUtils.copy(response, writer);
    String responseString = writer.toString();
    System.out.println(responseString);
    //Here goes something like assert(response.contains(refIdExpected));

    }
}

当我按原样运行时,我得到java.lang.IllegalArgumentException: argument type mismatch at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)。

我认为我可能使数据结构过于复杂,但现在它是以这种方式构建的并且似乎成功地提取了数据元素,我不知道如何重建。有人看到我在这里做错了吗?

这是整个血腥的例外:

java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at org.junit.runners.Parameterized$TestClassRunnerForParameters.createTestUsingConstructorInjection(Parameterized.java:186)
at org.junit.runners.Parameterized$TestClassRunnerForParameters.createTest(Parameterized.java:181)
at org.junit.runners.BlockJUnit4ClassRunner$1.runReflectiveCall(BlockJUnit4ClassRunner.java:244)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:241)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runners.Suite.runChild(Suite.java:127)
at org.junit.runners.Suite.runChild(Suite.java:26)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:202)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:65)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

【问题讨论】:

  • 矩阵在哪里/如何定义?异常还有更多信息吗?
  • 我会在上面添加。在类的开头,它被定义为static Object matrix; Exception 添加。
  • 嗯,问题,第 186 行是哪一行?
  • 这很奇怪。我深入到Parameterized.class,它只到第 65 行。让我看看我的 JUnit 库是否是最新的......
  • 我们开始吧,Parameterized.class 来自 185..187:private Object createTestUsingConstructorInjection() throws Exception {return getTestClass().getOnlyConstructor().newInstance(fParameters);}

标签: java collections multidimensional-array junit parameterized


【解决方案1】:

Arrays.asList 是这样定义的:

static <T> List<T> asList(T... a)

它需要一个可变长度的参数列表。当您调用这样的方法时,程序实际上会构建一个数组以传递给该方法-除非您使用一个参数调用它并且参数已经是一个数组。在这种情况下,它将传递您已经构建的数组。

这里的问题是,如果您将matrix 定义为Object,编译器不会将其视为数组。所以它构造了一个以matrix为元素的单元素数组。最终这会弄乱 JUnit 代码。

要使用asList,您需要确保参数具有实际的数组类型(在编译时)。但是,我不确定什么会起作用。你想要这样的东西:

return Arrays.asList((Object[][])matrix);

但我感觉类型转换在运行时会失败。您可能需要将matrix 声明为

Object[][] matrix;

然后代替

matrix = Array.newInstance(String.class, 2, tests.length);

说

matrix = new Object[2][tests.length];

(无论如何,我认为在这里使用Array.newInstance 没有任何好处,而不是只使用普通的new。)

【讨论】:

  • 这将清除数据结构,但是我将如何将该对象作为集合返回?
  • 如果您声明Object[][] matrix;,那么Arrays.asList(matrix) 应该可以工作。它将返回一个 ArrayList,其中每个元素都是一个 2 元素 Object[] 数组。
  • 实际上,JUnit Wiki 中的示例显示@Parameters 函数被声明为public static Collection&lt;Object[]&gt; data(),即[] 在Object 之后。您可能需要像这样修复 getTestParameters 声明。
  • 这似乎是正确的轨道,但是现在我在 JUnit 结果中看到每行的第一行被视为其自己的单个参数,并且每个都返回 java.lang.IllegalArgumentException: wrong number of arguments。我会继续破解它。谢谢!
  • @kyoob 我会考虑在没有Array 类的情况下尝试重写它。 Array 是 Java 的“反射”功能的一部分,反射是一种可用于特定目的的功能,但不能完成“正常”的事情。 (我很少使用它。)如果您可以使用普通数组索引来做您需要做的事情,那么您应该这样做;不必要地使用 Array 可能会导致问题,因为它会让你做一些你不应该做的事情,并且不会在编译时被捕获。
【解决方案2】:

我认为问题在于 JUnit @Parameters 方法需要返回一个对象数组列表,而您要向它传递一个字符串数组列表。由于二维数组基本上只是一个数组数组,因此将二维字符串数组 (String[][]) 传递到 Arrays.asList() 会返回一个 List&lt;String[]&gt;,即字符串数组的列表。

不过,当您设置它时,它确实很容易修复,只需将您传递给 Array 构造函数的 String.class 参数更改为 Object.class,您应该可以进行功能测试。

【讨论】:

  • 刚试过这个,不行。我切换了那个参数,所以它就像matrix = Array.newInstance(Object.class, 2, tests.length);,我在同一个地方得到了同样的错误。当我尝试这样做时,确实发生了变化的一件事是 JUnit 在它发出嘶哑之前的结果。以前看起来像test[0: [Ljava.lang.String;@5678b14],现在看起来像test[0: [Ljava.lang.Object;@4567b14]
  • 是的,我已经怀疑我最初认为是你的问题,毕竟不是,所以根据我对这个问题的评论,哪一行是 186?
  • 这里是:return getTestClass().getOnlyConstructor().newInstance(fParameters);
【解决方案3】:

由于我无法编辑 @ajb 的答案和后续的 cmets,我将把最终在这里起作用的内容放在它自己的答案中。

按照建议,我尝试将matrix 声明为Object[][],但只保留了表达式的其余部分和for 循环,但它仍然不起作用。这是newInstance 设置我的数组数组的方式,这让我遇到了麻烦。最后我把它拿出来并用这个替换了相关的块:

    //Same as before from XMLBeans methods:
    DisplayNameReferenceIdDoc = DisplayNameReferenceIdDocument.Factory.parse(refIdFile);
    displayNameReferenceId = DisplayNameReferenceIdDoc.getDisplayNameReferenceId();
    tests = displayNameReferenceId.getTestcaseArray();

    //new, simpler Object setup strategy:
    int i;
    Object[][] matrix = new Object[tests.length][2];
    for (i = 0; i < tests.length; i++) {
        matrix[i][0] = tests[i].getDisplayName();
        matrix[i][1] = tests[i].getRefId();
    };
    return Arrays.asList(matrix);

@Parameterized 测试运行程序按预期将值对发送到我的测试构造函数,然后该类开始按照我的@Test 块的指示愉快地进行 Web 服务调用。

在一个完美的世界中,XMLBeans 包本身会发回复杂订单项的多维数组,这样用户就不必进行这种争论。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-05
    • 2016-07-05
    相关资源
    最近更新 更多