Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JUnitParams not working with String array

Consider this test class, working with JUnit 4 and JUnitParams:

import static junitparams.JUnitParamsRunner.$;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;

import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(JUnitParamsRunner.class)
public class JUnitParamsExample {

    private int[] getIntArray() {
        int array[] = new int[2];
        array[0] = 1;
        array[1] = 2;
        return array;
    }

    public Object getInts() {
        return $($(getIntArray()));
    }

    @Parameters(method = "getInts")
    @Test
    public void testIntArray(int... values) {
        //
    }

    private String[] getStringArray() {
        String array[] = new String[2];
        array[0] = "a";
        array[1] = "b";
        return array;
    }

    public Object getStrings() {
        return $($(getStringArray()));
    }

    @Parameters(method = "getStrings")
    @Test
    public void testStringArray(String... values) {
        //
    }
}

The test method testIntArray runs fine, while testStringArray does not. The error output is:

java.lang.IllegalArgumentException: Cannot parse parameters. Did you use , as column separator? a
    at junitparams.internal.InvokeParameterisedMethod.castParamsFromString(InvokeParameterisedMethod.java:51)
    at junitparams.internal.InvokeParameterisedMethod.<init>(InvokeParameterisedMethod.java:35)
    at junitparams.internal.ParameterisedTestClassRunner.buildMethodInvoker(ParameterisedTestClassRunner.java:121)
    at junitparams.internal.ParameterisedTestClassRunner.parameterisedMethodInvoker(ParameterisedTestClassRunner.java:115)
    at junitparams.JUnitParamsRunner.methodInvoker(JUnitParamsRunner.java:425)
    at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:251)
    at junitparams.JUnitParamsRunner.runChild(JUnitParamsRunner.java:405)
    at junitparams.JUnitParamsRunner.runChild(JUnitParamsRunner.java:383)
    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.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.IllegalArgumentException: Parameter type cannot be handled! Only primitive types and Strings can be used.
    at junitparams.internal.InvokeParameterisedMethod.castParameterDirectly(InvokeParameterisedMethod.java:171)
    at junitparams.internal.InvokeParameterisedMethod.castAllParametersToProperTypes(InvokeParameterisedMethod.java:122)
    at junitparams.internal.InvokeParameterisedMethod.castParamsUsingConverters(InvokeParameterisedMethod.java:101)
    at junitparams.internal.InvokeParameterisedMethod.castParamsFromString(InvokeParameterisedMethod.java:49)

I'm wondering why and int array works and a string array does not. Is there a way to pass a string array to a varargs test method with junitparams?

EDIT: What I would like to write is something like this:

public Object getStrings() {
        return $($("Hello", "world"),
                $("Me", "You"),
                $("Dog", "Cat"));
}

such that all inner $-entries are passed to the varargs argument.

like image 510
Juergen Avatar asked Feb 05 '14 16:02

Juergen


1 Answers

Okay, basically it looks like it's a bug in JUnitParams. After applying the fix in the original answer below, the library still "unwraps" the String[] when we don't want it to - given the amount of conditional wrapping and unwrapping going on in the source, it seems that either the author is trying to be too clever for their own good, or they don't really know what they're expecting at any one time. The difference occurs in safelyCastParamsToArray, where in the int[] case it's passed an int[] and wraps it in an Object[], whereas in the String[] cases it's passed a String[] which is not wrapped because it can be cast to Object[] already.

You can make it work by making your method return a doubly-wrapped array, like this:

return $((Object)$((Object)getStringArray()));

Or just:

return new Object[] { new Object[] { getStringArray() } };

The int[] version only needs a single level of wrapping however, so you can just use:

return $(getIntArray());

rather than the "attempted double wrapping which is foiled by varargs" in your current code.

Personally I'm somewhat wary of all of this - it has a distinctly "black magic, tinker until it works" feeling which doesn't tend to be present in clean APIs...


Original answer

I believe the problem is that a String[] array is already an Object[], so you're ending up with it not being wrapped in the way that you want it to - varargs is treating the argument as as the whole array rather than one element of the array. You can fix this fairly easily though:

public Object getStrings() {
    Object array = getStringArray();
    return $(array);
}

Or just use a cast:

public Object getStrings() {
    return $((Object) getStringArray());
}

Or just ignore the $ method in this case:

public Object getStrings() {
    return new Object[] { getStringArray() };
}

(I very much doubt that you need $($(...)) in either case - you're just trying to make an array of arrays, right? So only a single level of wrapping is required, and you're only getting a single level of wrapping anyway, for precisely the same reason that your original code didn't work with the string array.)

like image 68
Jon Skeet Avatar answered Oct 25 '22 12:10

Jon Skeet