androidunit-testingkotlinjunit4parameterized-tests

Why is IllegalArgumentException thrown for a Parameterized JUnit test when given List<List<Any>>, but works with List<Array<Any>>


While debugging a Parameterized test, I realized the test wouldn't run if the parameters were passed as a List of Lists (List<List<Any>>), yet worked fine with a List of Arrays (List<Array<Any>>).

Example classes:

import com.google.common.truth.Truth
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized


@RunWith(Parameterized::class)
class TestWithList(val input: List<Int>, val output: Int) {

    companion object {
        @JvmStatic
        @Parameterized.Parameters
        fun data(): List<List<Any>> =
                listOf(
                        listOf(
                                listOf(1, 4, 3, 2),
                                4
                        )
                )

    }

    @Test
    fun `empty test`() {
        Truth.assertThat(true).isTrue()
    }
}

Throws

IllegalArgumentException: wrong number of arguments

import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized

@RunWith(Parameterized::class)
class TestWithArray(val input: List<Int>, val output: Int) {

    companion object {
        @JvmStatic
        @Parameterized.Parameters
        fun data(): List<Array<Any>> =
                listOf(
                        arrayOf(
                                listOf(1, 4, 3, 2),
                                4
                        )
                )

    }

    @Test
    fun `empty test`() {
        assertThat(true).isTrue()
    }
}

Runs perfectly.

Why does passing a List pass the wrong number of arguments?


Solution

  • I'm not familiar with parameterized testing with JUnit. However, I did find this. If you look at the method signature:

    public static Collection<Object[]> data() 
    

    In Kotlin, that is:

    fun data() : Collection<Array<Any>>
    

    You'll see it's a Collection, containing an Object array. List is a Collection, but a List is not an Array. It's a two-dimensional system, where the second dimension contains the arguments you want to pass, including length.

    Since they use an Array in the docs, I'd suggest you do so to.

    Recently, they introduced single parameters. The signature looks like this:

    @Parameters
    public static Iterable<? extends Object> data() 
    

    And again, List is an Iterable. However, in your case, this is the one you ended up calling without an array.

    As a result, you passed a single argument to a two-argument constructor, which threw the exception. You passed an Iterable>, which is not Collection<Array<Any>>. Which means you instead ended up creating a system that works for a single argument. Since the constructor takes two arguments, you get the exception. You pass a single argument (the List), but you need two arguments.

    This is why using an array works, but a List doesn't; if you use a list, it assumes you have a single argument constructor, where as with an Array, it takes multiple. As far as I can tell, JUnit doesn't support Lists instead of Arrays for multi-argument constructors.


    TL;DR: The reason arrays work and list don't is because it thinks the List is an argument for a single-argument constructor, where as with an Array, it could be any number of arguments.