Question

I'm fairly new to Java, and I'm certainly in over my head. Okay, disclaimer aside, here's my story:

I have a big list of video filenames and complementary strings called reference ID's in an XML file. The XML looks like this:

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

I've converted the XML schema into a class using XMLBeans and I'm able to import the data into a couple of arrays using the method documented here: 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);
    }

That's all happening in a @Parameterized method in my test class, which must return a collection. Here's the whole thing, zoomed out:

@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));

    }
}

When I run this as is I get java.lang.IllegalArgumentException: argument type mismatch at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method).

I think I might be over-complicating the data structure, but now that it's built that way and seems to be successfully pulling in the data elements, I don't know how else to rebuild. Anyone see what I'm doing wrong here?

Here's the whole gory exception:

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)
Was it helpful?

Solution

Arrays.asList is defined like this:

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

It takes a variable-length parameter list. When you call a method like this, the program actually builds an array to pass to the method--unless you call it with one argument and the argument is already an array. In that case, it will pass the array you've already built.

The problem here is that if you've defined matrix as an Object, the compiler won't see that as an array. So it constructs a one-element array with matrix as the element. Ultimately this messes up the JUnit code.

To use asList, you'll need to make sure that the parameter has an actual array type (at compile time). However, I'm not sure just what will work. You want something like this:

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

but I have a feeling the type cast will fail at run time. You may need to declare matrix as

Object[][] matrix;

and then instead of

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

say

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

(I don't see any benefit to using Array.newInstance here anyway, instead of using just a regular new.)

OTHER TIPS

I think the problem is that the JUnit @Parameters method needs to return a List of Object arrays and you're passing it a List of String Arrays. Since a two dimensional array is basically just an array of arrays, passing your twodimensional String array (String[][]) into Arrays.asList() returns a List<String[]>, a list of String arrays.

As you set it up though, it's really easy to fix, just change the String.class argument you pass to your Array constructor to Object.class and you should have functioning tests.

Since I can't edit @ajb's answer and subsequent comments I'll put what finally worked here, in its own answer.

As suggested, I'd tried declaring matrix as an Object[][] but left the rest of the expression and for loop alone, and it still didn't work. It was the newInstance way of setting up my array of arrays which was getting me into trouble. In the end I took that out and replaced the pertinent block with this:

    //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);

The @Parameterized test runner sent the value pairs to my test constructor as expected and the class began happily making web service calls as instructed by my @Test block.

In a perfect world the XMLBeans package itself would send back multidimensional arrays for complex line items so the user wouldn't have to do this wrangling.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top