Java’s varargs are for unit tests

At Devoxx last week, Joshua Bloch argued during his talk “The Evolution of Java: Past, Present, and Future” that varargs are only “somewhat useful”. I think he is overlooking some usages, particularly in tests. Here is my case.

A reminder on how varargs work: essentially, they allow the last parameter of a method to be made of zero to many values of the same type.
For example, a method like this:

int max(int... values) {...};

is used by code like that:

int maximum = max(1, 2, 7, 0);
int maximum = max(1);

In truth, that form is only moderately useful in production code. It turns out that, on my projects at least, it is not so common for methods to be called with varying number of parameters. And in those cases, it is often acceptable to simply overload a method with more parameters.

There are however at least two cases where varargs shine.

One is in utilities classes. For example, Math.max() only takes exactly two parameters. Which comparisons between 3 or more elements very awkward. If a varargs had been used, Math.max(3, Math.max(1, 4)) would be written Math.max(3, 1, 4). Agreed, that does not happen that often. But it does occasionally.
(I must admit that I do not understand exactly why Math.max() has not been modified to take a varargs nowadays)

However, the biggest benefit, I believe, is in the writing of tests.
Tests tend to contain lots of variations in the values passed to the code under test. And it is critical to keep the clutter to a minimum.
For example, here is how a typical series of tests might look like

@Test
public void should_find_the_longest_name_for_a_single_user() {
	List users = new ArrayList();
	users.add(new User("eric"));
	assertThat(findLongestName(users), is("eric"));
}
@Test
public void should_find_the_longest_name_when_shorted_is_first() {
	List users = new ArrayList();
	users.add(new User("eric"));
	users.add(new User("cecile"));
	assertThat(findLongestName(users), is("cecile"));
}
@Test
public void should_find_the_longest_name_when_longest_is_first() {
	List users = new ArrayList();
	users.add(new User("cecile"));
	users.add(new User("eric"));
	assertThat(findLongestName(users), is("cecile"));
}
@Test(expected = RuntimeException.class)
public void should_fail_when_search_for_the_longest_name_with_no_users() {
	assertThat(findLongestName(new ArrayList()), is("eric"));
}

Of course, they can be made a bit nicer using the varargs in Arrays.asList():

@Test
public void should_find_the_longest_name_for_a_single_user() {
	assertThat(findLongestName(asList(new User("eric"))), is("eric"));
}

@Test
public void should_find_the_longest_name_when_shorted_is_first() {
	assertThat(findLongestName(asList(new User("eric"),
		new User("cecile"))), is("cecile"));
}

@Test
public void should_find_the_longest_name_when_longest_is_first() {
	assertThat(findLongestName(asList(new User("cecile"),
		new User("eric"))), is("cecile"));
}

@Test(expected = RuntimeException.class)
public void should_fail_when_search_for_the_longest_name_with_no_users() {
	findLongestName(Arrays.asList());
}

However, the real goodness comes when writing our own builder method:

private static List users(String... names) {
	List users = new ArrayList();
	for (String name : names) {
		users.add(new User(name));
	}
	return users;
}

(side note: I like this type of methods to be private -so that I get notified when they are not used anymore- and static -mostly because they look nicer in italic, which makes it clearer that they are not part of the code being tested-)

which allow our tests to become:

@Test
public void should_find_the_longest_name_for_a_single_user() {
	assertThat(findLongestName(users("eric")), is("eric"));
}

@Test
public void should_find_the_longest_name_when_shorted_is_first() {
	assertThat(findLongestName(users("eric", "cecile")), is("cecile"));
}

@Test
public void should_find_the_longest_name_when_longest_is_first() {
	assertThat(findLongestName(users("cecile", "eric")), is("cecile"));
}

@Test(expected = RuntimeException.class)
public void should_fail_when_search_for_the_longest_name_with_no_users() {
	findLongestName(users());
}

Tests become a lot shorter, consistent, and easier to read. In fact, at this point, it is worth grouping the tests (at least those that do not throw an exception) into a single one:

@Test
public void should_find_the_longest_name_for_a_single_user() {
	assertThat(findLongestName(users("eric")), is("eric"));
	assertThat(findLongestName(users("eric", "cecile")), is("cecile"));
	assertThat(findLongestName(users("cecile", "eric")), is("cecile"));
}

@Test(expected = RuntimeException.class)
public void should_fail_when_search_for_the_longest_name_with_no_users() {
	findLongestName(users());
}

Final note: I prefer not to group those builder methods into a general utilities class — I tend to duplicate them in each test class. One reason is that I prefer keeping much of the test code close together. Another is that I tend to change the name of the method from test class to test class to make the code more fluent. Also, I think that DRY principles are not as important in the tests as they are in the production code.

About Eric Lefevre-Ardant

Independent technical consultant.
This entry was posted in conferences, java, tdd. Bookmark the permalink.

2 Responses to Java’s varargs are for unit tests

  1. David Wallace says:

    Another great article, Eric.

    Just another idea about the class that the builder methods live in; and trying to do DRY. My preference is to make such methods public and static, and put them in the test class for whichever type they are most closely related to. In your example, they’d be in the UserTest class. Public is OK, since they only ever end up in the test version of the build, not the production build.

    That way, when I come to write any test class that does some building on the User class, I know that I’m likely to find the builder method in UserTest. So I put the right static import at the top of my test class, and end up with the same concise, readable code that you’ve exhibited here.

  2. @David hm… I wouldn’t be happy having tests depend on each other. I realize that tests within the same test class are definitely closely dependent on each other, because of these local builder methods, but I feel this is acceptable.
    I wouldn’t do that across multiple tests, though.
    also, my builder methods tend to be fairly small (6 lines at the most), so it doesn’t feel so bad having several of them.

    Oh, another thing: I have found that I do not like to apply the same rules when writing test code and production code. Particularly, I do not believe that the DRY principle applies as much in test classes. However, the readability is a lot more important in the tests than it is in the production code.
    Several people I’ve talked to did seem to dislike this approach, though.

Comments are closed.