Test With Spring Course
Save Time by Writing Less Test Code
  • Home
  • Pre-Sales FAQ
  • Support
  • Log In

/ October 2, 2016 / petrikainulainen

Writing Parameterized Tests With JUnit 4

Lesson Progress:
← Back to Topic

After we have finished this lesson, we

  • Know how we can write parameterized tests by using the test runner provided by JUnit 4.
  • Understand why we shouldn’t write parameterized tests that use the test runner provided by JUnit 4.
  • Can write parameterized tests by using the JUnitParams library.

This lesson assumes that:

  • You are familiar with JUnit 4
  • You can use custom test runners
  • You know how you can run unit tests by using Maven or Gradle

Watch the Lesson

 

The text version of this lesson is given in the following:

What Is a Parameterized Test?

A parameterized test is a test case that is invoked by using a predefined input and expected output. These tests are typically used to test classes that have no external dependencies and that provide methods which have no side effects.

Let’s move on and take a look at the system under test.

Introduction to System Under Test

During this lesson we will write unit tests for a simple Calculator class. It has one method called sum() and this method returns the sum of two integers. The source code of the Calculator class looks as follows:

public class Calculator {

    public int sum(int first, int second) {
        return first + second;
    }
}

Next, we will write parameterized tests for the sum() method by using JUnit 4.

Writing Parameterized Tests With JUnit 4

We can write parameterized tests with JUnit 4 by following these steps:

First, we have to ensure that our unit tests are run by the Parameterized class. After we have configured the used test runner, the source code of our test class looks as follows:

import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

@RunWith(Parameterized.class)
@Category(UnitTest.class)
public class StandardCalculatorTest {

}

Second, we have to create a static method that returns a Collection of Object arrays, and annotate this method with the @Parameters annotation. This method creates the input provided to the tested object and describes the expected output.

After we have created this method, the source code of our test class looks as follows:

import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import java.util.Arrays;
import java.util.Collection;

@RunWith(Parameterized.class)
@Category(UnitTest.class)
public class StandardCalculatorTest {

    @Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][] {
                { 0, 0, 0 }, { 1, 1, 2 }
        });
    }
}

Third, we have to add the required fields into our test class. These fields contain the tested object, the input provided to the tested object, and the expected output.

After we have added these fields into our test class, its source code looks as follows:

import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import java.util.Arrays;
import java.util.Collection;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(Parameterized.class)
@Category(UnitTest.class)
public class StandardCalculatorTest {

    @Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][] {
                { 0, 0, 0 }, { 1, 1, 2 }
        });
    }

    private final Calculator calculator;
    private final int first;
    private final int second;
    private final int expectedSum;
}

Fourth, we have to write a constructor that takes the input and output data as constructor arguments. These arguments are provided by the custom test runner which obtains them by invoking the data() method. This constructor also creates the tested object.

After we have created this constructor, the source code of our test class looks as follows:

import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import java.util.Arrays;
import java.util.Collection;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(Parameterized.class)
@Category(UnitTest.class)
public class StandardCalculatorTest {

    @Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][] {
                { 0, 0, 0 }, { 1, 1, 2 }
        });
    }

    private final Calculator calculator;
    private final int first;
    private final int second;
    private final int expectedSum;

    public StandardCalculatorTest(int first, 
                                  int second, 
                                  int expectedSum) {
        
        this.calculator = new Calculator();
        this.first = first;
        this.second = second;
        this.expectedSum = expectedSum;
    }
}

Fifth, we have to write one unit test which verifies that the sum() method of the Calculator class is working as expected when we use the test data provided by the data() method.

After we have written this unit test, the source code of our test class looks as follows:

import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import java.util.Arrays;
import java.util.Collection;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(Parameterized.class)
@Category(UnitTest.class)
public class StandardCalculatorTest {

    @Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][] {
                { 0, 0, 0 }, { 1, 1, 2 }
        });
    }

    private final Calculator calculator;
    private final int first;
    private final int second;
    private final int expectedSum;

    public StandardCalculatorTest(int first, 
                                  int second, 
                                  int expectedSum) {
        
        this.calculator = new Calculator();
        this.first = first;
        this.second = second;
        this.expectedSum = expectedSum;
    }

    @Test
    public void shouldReturnCorrectSum() {
        int actualSum = calculator.sum(first, second);
        assertThat(actualSum).isEqualByComparingTo(expectedSum);
    }
}

When we run this test method, we notice that the shouldReturnCorrectSum() method is invoked twice by using the parameters which are configured in the data() method.

Additional Reading:

  • JUnit 4 Wiki: Parameterized Tests

Even though this approach works, I think that we shouldn’t use it because of the following reasons:

  • It requires a lot of boilerplate code. This makes our test hard to write and read.
  • Because the test data is passed as constructor arguments, we cannot write multiple test methods which use different test data.

Luckily for us, there is a better way. Let’s find out how we can simplify our parameterized tests by using the JUnitParams library.

Writing Parameterized Tests With JUnitParams

Getting the Required Dependencies

Before we can write parameterized tests with JUnitParams, we have to declare the JUnitParams dependency in our build script.

If we are using Maven, we have to add the following snippet into our pom.xml file:

<dependency>
	<groupId>pl.pragmatists</groupId>
	<artifactId>JUnitParams</artifactId>
	<version>1.0.5</version>
	<scope>test</scope>
</dependency>

If we are using Gradle, we have to add the following snippet into our build.gradle file:

testCompile(
	'pl.pragmatists:JUnitParams:1.0.5'
)

Let’s move on and write the same unit test by using the JUnitParams library.

Writing the Unit Test

We can ensure that the sum() method of the Calculator class is working as expected by following these steps:

First, we have to ensure that our unit tests are run by the JUnitParamsRunner class. After we have configured the used test runner, the source code of our test class looks as follows:

import junitparams.JUnitParamsRunner;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;

@RunWith(JUnitParamsRunner.class)
@Category(UnitTest.class)
public class JUnitParamsCalculatorTest {

}

Second, we have to write one test method by following these steps:

  1. Add a new test method into our test class. This method takes the provided input and the expected output as method parameters.
  2. Ensure that sum() method of the Calculator class is working as expected.

After we have written this test method, the source code of our test class looks as follows:

import junitparams.JUnitParamsRunner;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(JUnitParamsRunner.class)
@Category(UnitTest.class)
public class JUnitParamsCalculatorTest {

    @Test
    public void shouldReturnCorrectSum(int first, 
									   int second, 
									   int expectedSum) {
        Calculator calculator = new Calculator();
        int actualSum = calculator.sum(first, second);
        assertThat(actualSum).isEqualByComparingTo(expectedSum);
    }
}

Third, we have to configure the parameters that are passed to our test method by following these steps:

  1. Annotate the test method with the @Parameters annotation.
  2. Configure the input provided to the Calculator object and the expected output.

After we have configured the parameters of our parameterized test, the source code of our test class looks as follows:

import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(JUnitParamsRunner.class)
@Category(UnitTest.class)
public class JUnitParamsCalculatorTest {

    @Test
    @Parameters({
            "0, 0, 0",
            "1, 1, 2"
    })
    public void shouldReturnCorrectSum(int first, 
                                       int second, 
                                       int expectedSum) {
        Calculator calculator = new Calculator();
        int actualSum = calculator.sum(first, second);
        assertThat(actualSum).isEqualByComparingTo(expectedSum);
    }
}

As we can see, JUnitParams solves the problems which we found from the parameterized tests that use the test runner provided by JUnit. It has no boilerplate code, and because the parameters are passed directly to the test method, we can have multiple test methods that use different parameters. We can even add normal test methods into our test class.

Also, one additional benefit of using JUnitParams is that we can implement custom data providers and read input data from external data sources such as CSV files.

Additional Reading:

  • The Reference Documentation of JUnitParams

Let’s summarize what we learned from this lesson.

Summary

This lesson has taught us four things:

  • We shouldn’t use the test runner provided by JUnit 4 because it forces us to write boilerplate code that makes our tests hard to write and read.
  • If we want to use the JUnitParams library, we have to declare the JUnitParams dependency in our build script.
  • The JUnitParams library solves the problems caused by JUnit 4.
  • The JUnitParams library supports custom data provider classes and external data sources such as CSV files.

Get the source code from Github

← Previous Lesson Next Lesson →

Can I help you?

This is a free sample lesson of my Test With Spring course. If this lesson helped you to solve your problem, you should find out how my testing course can help you.

Support and Privacy

  • Pre-Sales FAQ
  • Support
  • Cookie Policy
  • No Bullshit Privacy Policy
  • No Bullshit Terms and Conditions

Test With Spring Course

  • Starter Package
  • Intermediate Package
  • Master Package

Free Sample Lessons

  • Introduction to JUnit 4
  • Introduction to Unit Testing
  • Introduction to Integration Testing
  • Introduction to End-to-End Testing
  • Introduction to Spock Framework
  • Introduction to Integration Testing – Spock Edition
  • Writing End-to-End Tests With Spock Framework

Copyright Koodikupla Oy 2016 — Built on Thesis by Themedy