Parameterized tests allow you to run the same test multiple times with different inputs. This is particularly useful when you want to test a method with a variety of data sets without writing multiple test methods. In JUnit, parameterized tests are a powerful feature that can help you ensure your code works correctly across a range of inputs.

Key Concepts

  1. Parameterized Tests: Tests that are executed multiple times with different sets of parameters.
  2. @ParameterizedTest Annotation: Marks a method as a parameterized test.
  3. @ValueSource Annotation: Provides a simple way to specify a single array of values.
  4. @CsvSource Annotation: Allows you to specify multiple sets of parameters in a CSV format.
  5. @MethodSource Annotation: Uses a method to provide parameters.
  6. @ArgumentsSource Annotation: Uses a custom provider to supply parameters.

Why Use Parameterized Tests?

  • Efficiency: Reduces the need to write multiple test methods for different inputs.
  • Readability: Makes tests more readable and maintainable by consolidating similar tests.
  • Coverage: Ensures that your code is tested against a wide range of inputs, improving test coverage.

Basic Example

Let's start with a simple example to illustrate how parameterized tests work. Suppose we have a method that checks if a number is even:

public class NumberUtils {
    public static boolean isEven(int number) {
        return number % 2 == 0;
    }
}

We want to test this method with various inputs to ensure it works correctly. Here's how you can write a parameterized test for this method:

import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

public class NumberUtilsTest {

    @ParameterizedTest
    @ValueSource(ints = {2, 4, 6, 8, 10})
    void testIsEvenWithEvenNumbers(int number) {
        assertTrue(NumberUtils.isEven(number));
    }

    @ParameterizedTest
    @ValueSource(ints = {1, 3, 5, 7, 9})
    void testIsEvenWithOddNumbers(int number) {
        assertFalse(NumberUtils.isEven(number));
    }
}

Explanation

  • @ParameterizedTest: Marks the method as a parameterized test.
  • @ValueSource: Provides an array of integers to be used as parameters for the test method.
  • assertTrue/assertFalse: Asserts that the method returns the expected result for each input.

Using @CsvSource

If you need to test a method with multiple parameters, you can use the @CsvSource annotation. For example, let's test a method that adds two numbers:

public class MathUtils {
    public static int add(int a, int b) {
        return a + b;
    }
}

Here's how you can write a parameterized test for this method:

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

public class MathUtilsTest {

    @ParameterizedTest
    @CsvSource({
        "1, 1, 2",
        "2, 3, 5",
        "3, 5, 8",
        "4, 6, 10"
    })
    void testAdd(int a, int b, int expected) {
        assertEquals(expected, MathUtils.add(a, b));
    }
}

Explanation

  • @CsvSource: Provides multiple sets of parameters in CSV format.
  • assertEquals: Asserts that the method returns the expected result for each set of parameters.

Practical Exercise

Exercise

Write a parameterized test for a method that checks if a string is a palindrome. A palindrome is a word that reads the same backward as forward.

public class StringUtils {
    public static boolean isPalindrome(String str) {
        String cleaned = str.replaceAll("\\s+", "").toLowerCase();
        return new StringBuilder(cleaned).reverse().toString().equals(cleaned);
    }
}

Solution

import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

public class StringUtilsTest {

    @ParameterizedTest
    @ValueSource(strings = {"madam", "racecar", "level", "radar", "noon"})
    void testIsPalindromeWithPalindromes(String str) {
        assertTrue(StringUtils.isPalindrome(str));
    }

    @ParameterizedTest
    @ValueSource(strings = {"hello", "world", "java", "programming", "junit"})
    void testIsPalindromeWithNonPalindromes(String str) {
        assertFalse(StringUtils.isPalindrome(str));
    }
}

Explanation

  • @ValueSource: Provides an array of strings to be used as parameters for the test method.
  • assertTrue/assertFalse: Asserts that the method returns the expected result for each input.

Conclusion

Parameterized tests in JUnit allow you to run the same test with different inputs, making your tests more efficient and comprehensive. By using annotations like @ParameterizedTest, @ValueSource, and @CsvSource, you can easily create parameterized tests that cover a wide range of scenarios. In the next topic, we will delve deeper into creating parameterized tests using different sources of parameters.

© Copyright 2024. All rights reserved