Java Programming Tutorials

Java programming tutorials with many code examples!

JUnit Parameterized tests

JUnit Parameterized Tests are JUnit’s way to execute one test multiple times, but with different data sets. This is commonly known as data-driven testing.

Basically such test classes are executed many times, but using series of data. To do that we have to do a few things:

  • run test class with special Test Runner: org.junit.runners.Parameterized
  • create public static void method that will return a collection of tables, which are sets of data
  • annotate data factory method with @Parameterized annotation to let know JUnit which one is it
  • create a constructor matching number of data items in one data set table (alternatively you can make fields public and annotate them with @Parameter to inject dataset value).

It may look complicated, but it is not. Just look at the examples.

If you are familiar with Spock Framework most probably you have applied that using incredibly cool where blocks.

JUnit Parameterized Test Example

In the first example we’re going to use the constructor approach. Each set of data (table in the collection) consists of three values: a, b, and result. JUnit will instantiate MinimalNumberFinderTest as many times as there the collection size – 3 in this case – and will pass through the constructor appropriate values. We assign them to fields and use in the test.
Note that name attribute of @Parameters annotation is optional and is used to set readable test name – strongly encouraged! :-)

package com.farenda.junit;

import org.junit.Test;
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.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;

@RunWith(Parameterized.class)
public class MinimalNumberFinderTest {

    // with "name" to have readable test name
    @Parameters(name = "minimum of {0} and {1} is {2}")
    public static Collection<Object[]> prepareTestData() {
        return Arrays.asList(new Integer[][] {
                // a, b, result
                {1,   2,  1},
                {23, 12, 12},
                {-7, -9, -9}
        });
    }

    private int a;
    private int b;
    private int result;

    public MinimalNumberFinderTest(int a, int b, int result) {
        this.a = a;
        this.b = b;
        this.result = result;
    }

    @Test
    public void shouldFindMinValue() {
        assertThat(Math.min(a, b), equalTo(result));
    }
}

In the next example we use @Parameter annotation instead of test class constructor. As in the previous example JUnit will instantiate the test class as many times as the size of dataset collection, but instead of passing the data through the constructor it will set annotated fields. Note that the fields must be public and annotation takes table index as parameter to inject correct value from dataset – we’ve skipped the parameter for the first field, because the default value is 0 anyway.

package com.farenda.junit;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;

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

import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;

@RunWith(Parameterized.class)
public class MaximumNumberFinderTest {

    @Parameters(name = "maximum of {0} and {1} is {2}")
    public static Collection<Object[]> prepareTestData() {
        return Arrays.asList(new Integer[][] {
              // a,   b,  result
                {1,   2,  2},
                {23, 12, 23},
                {-7, -9, -7}
        });
    }

    @Parameter
    public int a;

    @Parameter(1)
    public int b;

    @Parameter(2)
    public int result;

    @Test
    public void shouldFindMaxValue() {
        assertThat(Math.max(a, b), equalTo(result));
    }
}

How pros do it

In the following test from Spring WebSockets you can see that JUnit Parameterized Tests are not constrained only to numbers, as can be seen in many simple examples, but to any objects:

/*
 * Copyright 2002-2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.socket.config.annotation;

import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.AbstractWebSocketIntegrationTests;
import org.springframework.web.socket.JettyWebSocketTestServer;
import org.springframework.web.socket.TomcatWebSocketTestServer;
import org.springframework.web.socket.UndertowTestServer;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.client.jetty.JettyWebSocketClient;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import org.springframework.web.socket.server.HandshakeHandler;
import org.springframework.web.socket.sockjs.transport.handler.WebSocketTransportHandler;

import static org.junit.Assert.*;

/**
 * Integration tests for WebSocket Java server-side configuration.
 *
 * @author Rossen Stoyanchev
 */
@RunWith(Parameterized.class)
public class WebSocketConfigurationTests extends AbstractWebSocketIntegrationTests {

        @Parameters(name = "server [{0}], client [{1}]")
        public static Iterable<Object[]> arguments() {
                return Arrays.asList(new Object[][] {
                                {new JettyWebSocketTestServer(), new JettyWebSocketClient()},
                                {new TomcatWebSocketTestServer(), new StandardWebSocketClient()},
                                {new UndertowTestServer(), new StandardWebSocketClient()}
                });
        }


        @Override
        protected Class<?>[] getAnnotatedConfigClasses() {
                return new Class<?>[] { TestConfig.class };
        }

        @Test
        public void registerWebSocketHandler() throws Exception {
                WebSocketSession session = this.webSocketClient.doHandshake(
                                new AbstractWebSocketHandler() {}, getWsBaseUrl() + "/ws").get();

                TestHandler serverHandler = this.wac.getBean(TestHandler.class);
                assertTrue(serverHandler.connectLatch.await(2, TimeUnit.SECONDS));

                session.close();
        }

        @Test
        public void registerWebSocketHandlerWithSockJS() throws Exception {
                WebSocketSession session = this.webSocketClient.doHandshake(
                                new AbstractWebSocketHandler() {}, getWsBaseUrl() + "/sockjs/websocket").get();

                TestHandler serverHandler = this.wac.getBean(TestHandler.class);
                assertTrue(serverHandler.connectLatch.await(2, TimeUnit.SECONDS));

                session.close();
        }


        @Configuration
        @EnableWebSocket
        static class TestConfig implements WebSocketConfigurer {

                @Autowired
                private HandshakeHandler handshakeHandler; // can't rely on classpath for server detection

                @Override
                public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
                        registry.addHandler(serverHandler(), "/ws")
                                .setHandshakeHandler(this.handshakeHandler);
                        registry.addHandler(serverHandler(), "/sockjs").withSockJS()
                                .setTransportHandlerOverrides(new WebSocketTransportHandler(this.handshakeHandler));
                }

                @Bean
                public TestHandler serverHandler() {
                        return new TestHandler();
                }
        }

        private static class TestHandler extends AbstractWebSocketHandler {

                private CountDownLatch connectLatch = new CountDownLatch(1);

                @Override
                public void afterConnectionEstablished(WebSocketSession session) throws Exception {
                        this.connectLatch.countDown();
                }
        }

}
Share with the World!