Less unit test code doesn’t necessarily result into poor coverage. I’ve recently entered the paradigm of parameterized tests when refactoring a larger test suite. I ended up writing significantly less code without compromising on the extent. In fact, adding a few corner cases was almost a no-brainer. In this post I’d like to share my experience with JUnitParams and discuss advantages of parameterized unit tests.

Suppose I have just implemented a few of elementary sorting algorithms (Insertion, Quick and Selection Sort in this case) and can’t wait to test it.
public interface ISort {
 // Identifies the algorithm
 String getName();
 // Sorts the given numeric sequence
 Result sort(int... x);
 }
 // Implementation - details are not important just now
 public class InsertSort implements ISort {..}
 public class QuickSort implements ISort {..}
 public class SelectSort implements ISort {..}
Not only do I want to check if the given numeric sequence gets sorted, but I am also interested to see that the sorting was done in sync with the expected algorithm. For that reason I keep track of individual iterations so that they can be checked later on.
public class Result {
 private String algorithm;
 private List<int[]> steps = new ArrayList<int[]>();
 ..
 @Override
 public String toString() {
  // pretty-prints the sorting result, includes individual steps
 }
}
After a while I come up with fairly decent test code. I even have a customized builder, just to keep things clean and tidy.
public class StandardSortTest {
  private InsertSort insertSort = new InsertSort();
  ..
  @Test
  public void insertSortNull() {
    assertResult(insertSort.sort(null),
     new ResultBuilder(insertSort.getName()).build());
  }
  @Test
  public void insertSortEmpty() {
    assertResult(insertSort.sort(new int[] {}),
     new ResultBuilder(insertSort.getName()).build());
  }
  @Test
  public void insertSort() {
    assertResult(insertSort.sort(input),
        new ResultBuilder(insertSort.getName())
          .addStep(3, 7, 4, 9, 5, 2, 6, 1)
          .addStep(3, 7, 4, 9, 5, 2, 6, 1)
          .addStep(3, 4, 7, 9, 5, 2, 6, 1)
          .addStep(3, 4, 7, 9, 5, 2, 6, 1)
          .addStep(3, 4, 5, 7, 9, 2, 6, 1)
          .addStep(2, 3, 4, 5, 7, 9, 6, 1)
          .addStep(2, 3, 4, 5, 6, 7, 9, 1)
          .addStep(1, 2, 3, 4, 5, 6, 7, 9)
          .build()
    );
  }
  // etc, similar for the other 2 sorting algorithms
}
Despite all the efforts however, I couldn’t entirely remove a certain amount of repetitive boilerplate. My test methods might be one-liners, but clearly they only differ in subtle details.
 
Let’s take look how the situation changes when I arrive at the conclusion to parameterize my tests.
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.runner.RunWith;
..
@RunWith(JUnitParamsRunner.class)
public class ParameterizedSortTest {
  @Test
  @Parameters
  public void sort(ISort sortAlg, int[] toSort, Result expectedResult) {
   Result result = sortAlg.sort(toSort);
   Assert.assertEquals(result.toString(), expectedResult.toString());
  }
  // Provides parameters
  // see: https://code.google.com/p/junitparams
  private Object[] parametersForSort() {..}
}
Suddenly, the actual functional code shrinks to a single method. That’s for the boiler plate, though.. Isn’t it somewhat too vague and generic now? Test report matters after all, and with many test methods in place it was easy to tell what implementation aspect didn’t live up to the expectations: 

Someone forgot to check those unwieldy null values


Well, let’s see what the report would look like in case of the parameterized tests comprising a single test method:

Hover over effect reveals parameter values (captured in IntelliJ IDEA 12)

As you can see, the results are determined by the sets of passed parameters and not by the number of written test methods. I find it highly useful as it allows for data-driven tests with a minimal functional overhead.

Thanks for reading up to this point and I hope you feel inspired. Please find a complete example under the link below. The example demoes an additional feature – test data providers – which allows to keep data sets separate from the actual test code.

Source Code

 

Tomas Zezula

Hello! I'm a technology enthusiast with a knack for solving problems and a passion for making complex concepts accessible. My journey spans across software development, project management, and technical writing. I specialise in transforming rough sketches of ideas to fully launched products, all the while breaking down complex processes into understandable language. I believe a well-designed software development process is key to driving business growth. My focus as a leader and technical writer aims to bridge the tech-business divide, ensuring that intricate concepts are available and understandable to all. As a consultant, I'm eager to bring my versatile skills and extensive experience to help businesses navigate their software integration needs. Whether you're seeking bespoke software solutions, well-coordinated product launches, or easily digestible tech content, I'm here to make it happen. Ready to turn your vision into reality? Let's connect and explore the possibilities together.