Writing a parameterized JUnit test

JUnit 4 supports parameterized tests.  There are a few things that confuse me about how you set up your test class for it, though.  Let’s see what it would look like if what we wanted was to run the same test class, using a different Map<String,String> of settings each time.

The Normal Things

There are some elements of setup for using the parameterized test that didn’t confuse me.  Let’s briefly list them:

  • The test class needs to be decorated with the @RunWith(Parameterized.class) annotation
  • You need a public static data-providing method, decorated with the @Parameters annotation
  • Each test method is decorated with @Test (as usual)

Things I Found Confusing

The Data-Providing Method’s Return Type

The data-providing @Parameters method has to return a Collection<> of arrays.  Now, ever since at least Principles of Programming I & II in college, I’ve had trouble remembering which subscript is which when you have a multidimensional array.  So when I saw this helpful example, my brain got stuck on line 1 of what the @Parameters method was returning:

  return Arrays.asList(new Object[][] {
   {"22101", true },
   {"221x1", false },
   {"22101-5150", true },
   {"221015150", false }});

I couldn’t think how this would translate to my maps I wanted to run with.

The Special Constructor

When you’re using the JUnit 4 parameterized test, your test class needs to have a constructor that takes one set of the parameters and stores them to fields in the test class for use during that run.  But I couldn’t figure out — should my constructor expect a Map<String, String>[]?  A Map<String, String>?

Figuring it Out

There were a few different facets that it helped me understand:

Each Element of the Collection is a set of Parameters

The reason the @Parameters method must return a collection of Arrays is because each Array holds the parameters that are needed for one test scenario.  So if my test class needed a Map, an int, and a boolean, the @Parameters method would return a collection of three-element arrays — each array containing the parameters for one configuration of the test class.  This leads to the next facet…

The Test Class Constructor Should Accept One Parameter for Each Element of the Array

The JUnit parameterized test mechanism expects to instantiate the test class by calling a constructor that has the same number of arguments as there are elements in the current parameters array*.  If my test class FooTest needed a Map, an int, and a boolean each time, the constructor might look something like this:

    public FooTest(Map<String, String> map, int value, boolean flag) {
        //...
    }

…and my @Parameters method would need to return a Collection of Arrays of Object (“of Object” since for a given array, the three elements would be of different types).

*I haven’t read or tested to see if the JUnit mechanism supports a collection of jagged arrays of configuration parameters such that (for instance) sometimes the test class might be instantiated using the two-arg constructor, other times using its three-arg constructor…

Store the Parameters in Private Fields in your Test Class

What you’d normally do is store the parameters you get constructed with to private fields, for use by the @Test methods:

@RunWith(Parameterized.class)
class FooTest {
    private Map<String, String> map;
    private int value;
    private boolean flag;

    public FooTest(Map<String, String> map, int value, boolean flag) {
        this.map = map;
        this.value = value;
        this.flag = flag;
    }

    //...
}

The Application to My Case

I think I was more confused because I only needed one parameter — a Map<String, String> — so it wasn’t apparent to me why the Collection of Object Arrays was needed.

So the “hard parts” of my test class end up looking something like this:


@RunWith(Parameterized.class)

class FooTest {

    private Map<String, String> map;

    @Parameters
    public static Collection<Object[]> configs() {
        Map<String, String> map1 = new HashMap<String, String>();
        map1.put("Name", "Bill");
        map1.put("Favourite Color", "Blue");

        Map<String, String> map2 = new HashMap<String, String>();
        map2.put("Name", "Sam");
        map2.put("Favourite Color", "Plaid");

        return Arrays.asList(new Object[][] {
                { map1 },
                { map2 }
        });

    public FooTest(Map<String, String> map) {
        this.map = map;
    }

    //...
}

(Maybe nobody else needed that explanation, but it helped me!  :)

About these ads

,

  1. #1 by Ashok on September 22, 2008 - 10:20 am

    It is really good information to have.
    Thanks for this info.

    -Ashok

  2. #2 by danielmeyer on September 22, 2008 - 10:23 am

    Thanks for the comment, Ashok! I never realized till I started blogging how encouraging it is when people write and say what they found helpful.

  3. #3 by Rebeccah on October 17, 2008 - 2:39 pm

    I went through similar machinations, as I have a bunch of maps for cases. I couldn’t get maps to work because of Java 5 generics issues. I ended up splitting the maps up into arrays of name-value pairs, so I had a List of arrays. I eventually got all of that solved.

    My remaining problem is that I have a LOT of cases (I’m validating using actual patient data and the results of a different analysis process), and I want to read them in from a file – which means I don’t know ahead of time how big to dimension my array of arrays. I’ve got a method in a helper class that actually reads in the data and formats it into a List of arrays. My various test classes call that method (statically) to populate my Collection of parameters. If I go through an intermediate step of creating a 2-dimensional array, AND I initialize the size of the array with literals, then I can do an Arrays.asList() and I’m fine. But If I initialize the size with a variable (even a final variable), or if I skip the arrays step altogether, I get a “No runnable methods” error.

    Grrr!

    Any thoughts?

    Rebeccah

  4. #4 by danielmeyer on October 20, 2008 - 11:38 am

    Rebeccah,
    I’m with you through the part where your helper class gets the data out of the file and into a List of arrays. But I’m not visualizing the intermediate step where you’re having to create a 2-dimensional array — is this in your @Parameters method in each test class?

    I have a helper method too, but it returns a List<Object[]>, which I then just return from the @Parameters method — I have not had to do a multidimensional array intermediate step.

    Would you like to post the code of your @Parameters method?

  5. #5 by sadiq on June 21, 2009 - 8:41 am

    Thanks! quick help was needed and I got it

  6. #6 by Niels on September 12, 2009 - 4:29 pm

    If you have many parameters to your test, it can simplify your test to have a simple internal static class TestParams with appropriate fields and pass that to the constructor.

    I think it would make the developers life easier and typesafer, if JUnit also allowed your @Parameters to return a Collection if your testclass had a constructor taking a single T.

  7. #7 by Niels on September 14, 2009 - 5:25 am

    In the above comment, “Collection” should have been Collection<T>

  8. #8 by Ankit Jhalaria on December 3, 2010 - 3:46 pm

    Thanks a lot!
    Your example helped me a lot.

  9. #9 by buddy on April 27, 2011 - 3:30 am

    daniel, that is a good one!

  10. #10 by kiran on July 5, 2011 - 2:12 am

    Thanks for the valuable info

  11. #11 by Raghvendra Singh on January 18, 2012 - 2:58 pm

    What will be the timeout in this case? I mean if you had only one parameter and the test timeout is 10 mins, and if you add two more parameters to be run with then would the timeout go to 30 mins?

  12. #12 by danielmeyer on January 18, 2012 - 3:03 pm

    Raghvendra, I’m sorry — I’m not in a Java environment right now and I can’t answer your question. I hope you are able to find the answer.

  13. #13 by Heinz on March 30, 2012 - 5:20 am

    thanks already for the great intro!!!
    Would it be possible to pose one complete set of a test? (how to set up the parameters as well as using the parameters…)
    Parameterized Junit tests seem to me as a really great thing to use, but unfortunatelly i can’t get the variables back out of the list… -maybe it would already be a great help to show how to get the variables back out of the list-constuction…
    THANKS a lot!!

  14. #14 by tomekkaczanowski on April 12, 2012 - 4:59 pm

    IMHO default implementation of parameterized JUnit tests is awkward and hardly usable. There are two things you could do about it:
    – use JUnit Params (http://code.google.com/p/junitparams/), which copies what TestNG offers out-of-the-box
    – switch to TestNG

  15. #15 by amr lotfy on April 23, 2012 - 11:22 am

    Thank you very much !, that was very helpful :)

  16. #16 by Siraj on November 6, 2012 - 12:26 pm

    Excellent information, it helped a lot.
    Thank you

  17. #17 by Piotrek on December 5, 2012 - 5:48 pm

    when you know all parameters *before* you run test, there is a simpler/cleaner way. check zohhak project (zohhak.googlecode.com)

  1. JUnit 4 Vs TestNG – Comparison | unittest
  2. Outrospective.org » Blog Archive » Junit’s Theory’s as interprested by Schauderhaft and Groovy
  3. JUnit parameterized test with Spring autowiring AND transactions
  4. JUnit 4 Vs TestNG – Comparison | 码农网
  5. JUnit 4 与 TestNG 对比(翻译) | 天天三国杀

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.