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
- Parameterized Tests: Tests that are executed multiple times with different sets of parameters.
- @ParameterizedTest Annotation: Marks a method as a parameterized test.
- @ValueSource Annotation: Provides a simple way to specify a single array of values.
- @CsvSource Annotation: Allows you to specify multiple sets of parameters in a CSV format.
- @MethodSource Annotation: Uses a method to provide parameters.
- @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:
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:
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.
JUnit Course
Module 1: Introduction to JUnit
Module 2: Basic JUnit Annotations
- Understanding @Test
- Using @Before and @After
- Using @BeforeClass and @AfterClass
- Ignoring Tests with @Ignore
Module 3: Assertions in JUnit
Module 4: Parameterized Tests
- Introduction to Parameterized Tests
- Creating Parameterized Tests
- Using @ParameterizedTest
- Custom Parameterized Tests
Module 5: Test Suites
Module 6: Mocking with JUnit
Module 7: Advanced JUnit Features
Module 8: Best Practices and Tips
- Writing Effective Tests
- Organizing Test Code
- Test-Driven Development (TDD)
- Continuous Integration with JUnit