如何将多维数组作为参数集合供JUnit测试返回?

3

我是一个相对新手的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)

矩阵在哪里/如何定义?此外,异常是否有更多信息? - BevynQ
在类的开头,它被定义为 static Object matrix;。同时也添加了异常处理。 - kyoob
嗯,问题是,第186行是哪一行? - Bart Enkelaar
很奇怪。我深入到 Parameterized.class,但它只到第65行。让我看看我的 JUnit 库是否最新... - kyoob
这里是来自 185..187 的 Parameterized.classprivate Object createTestUsingConstructorInjection() throws Exception {return getTestClass().getOnlyConstructor().newInstance(fParameters);} - kyoob
这一切似乎很复杂,只是为了将一些数据传递到测试类中进行测试。 为什么不使用一个带有 @Before 注释的设置方法,然后填充一个 Map<String, String> 呢? 如果您的担忧是您只想读取和填充一次,那么在读取文件之前,请先检查 if(map.isEmpty()) - Dan Temple
3个回答

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相比。

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

0

由于我无法编辑@ajb的答案和后续评论,因此我将在此处放置最终有效的内容,作为自己的答案。

如建议所述,我尝试将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包本身会为复杂的行项目发送多维数组,以便用户不必进行这种操作。


0

我认为问题在于JUnit的@Parameters方法需要返回一个对象数组的列表,而你传递了一个字符串数组的列表。由于二维数组基本上只是一个数组的数组,将你的二维字符串数组(String[][])传递给Arrays.asList()会返回一个List<String[]>,即一个字符串数组的列表。

不过,按照你的设置,修复起来非常容易,只需将你传递给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] - kyoob
是的,我已经怀疑最初认为是你的问题的东西并不是,所以根据我在问题上的评论,第186行是哪一行? - Bart Enkelaar
这里是:return getTestClass().getOnlyConstructor().newInstance(fParameters); - kyoob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接