What Are Parameterized Tests and Why Do They Matter

Marina Torres, MCS
3 min readJun 14, 2024

--

As a developer, I can’t emphasize enough how crucial unit tests are. Writing high-quality unit tests can prevent many bugs from reaching production.

However, unit tests can become tedious to write, especially when the same logic needs to be tested repeatedly for multiple scenarios. This repetition makes the test files less readable, increases the risk of overlooking scenarios, and ultimately becomes a time-consuming and boring task.

This is where parameterized tests come to the rescue.

What are Parameterized tests?

Parameterized tests are a type of test that enables you to evaluate the same logic for various scenarios. Rather than creating multiple tests for the same functionality, you can write a single test that can accommodate different inputs and cover various cases.

When should we use parameterized tests?

When testing the same logic repeatedly with different input values, you may find yourself copying and pasting the same unit test multiple times.

When this happens, you should consider using parameterized tests to adhere to the DRY (Don’t Repeat Yourself) principle.

Parameterized tests should be simple, easy to understand, and should not include logic to distinguish between scenarios. The function name should be generic but still clearly define the purpose of the test.

Photo by Ian Dooley from Unsplash

Advantages of parameterized tests:

1. Improved Test Coverage

Allows testing the same logic with multiple sets of inputs, covering more edge cases and scenarios.

2. Reduced Code Duplication

DRY Principle: Follows the “Don’t Repeat Yourself” principle by reducing redundancy, and making the test codebase cleaner.

3. Ease of Maintenance

Easier to update a single parameterized test than modifying multiple individual tests.

4. Better Readability and Organization

Clear input-output relationships improve the readability and understanding of the test logic.

When to use parameterized tests:

  • The tested logic is simple.
  • You are testing the same function but for different scenarios (i.e., different input values).
  • The assertion logic is the same for every test.
  • When the tests are simple enough to generalize a test function.

When NOT to use parameterized tests:

  • When you are testing different functionalities.
  • When the parameterized test has too many arguments.
  • When the logic being tested is too complex.
  • When tests require unique initializations for each scenario.

Example of a good parameterized test

Simple and easy to understand. The logic being tested is also simple and the number of input parameters is low.

class PalindromeCheckerTest {

private val checker = PalindromeChecker()

@ParameterizedTest
@MethodSource("provideStringsForIsPalindrome")
fun `should test palindrome checker with various inputs`(input: String, expected: Boolean) {
assertEquals(expected, checker.isPalindrome(input))
}

fun provideStringsForIsPalindrome() = listOf(
Arguments.of("racecar", true),
Arguments.of("radar", true),
Arguments.of("level", true),
Arguments.of("A man a plan a canal Panama", true),
Arguments.of("No lemon no melon", true),
Arguments.of("Able was I ere I saw Elba", true),
Arguments.of("hello", false),
Arguments.of("world", false),
Arguments.of("not a palindrome", false),
Arguments.of("Kotlin", false),
)
}

Example of a bad parameterized test

With too many arguments, the logic of the function tested is complex and not intuitive.

class UserAuthenticatorTest {

private val authenticator = UserAuthenticator()

@ParameterizedTest
@MethodSource("provideLoginData")
fun `test validate login with various inputs`(username: String, password: String, isLoggedIn: Boolean, accountStatus: String,
failedAttempts: Int, isTwoFactorEnabled: Boolean, expectedOutcome: Boolean) {
assertEquals(expectedOutcome, authenticator.validateLogin(username, password, isLoggedIn, accountStatus,
failedAttempts, isTwoFactorEnabled))
}

fun provideLoginData() = listOf(
Arguments.of("user1", "correct_password", false, "active", 0, false, true),
Arguments.of("user2", "wrong_password", false, "active", 1, false, false),
Arguments.of("user3", "correct_password", false, "locked", 0, false, false),
Arguments.of("user4", "correct_password", false, "active", 3, false, false),
Arguments.of("user5", "correct_password", false, "active", 2, true, true),
Arguments.of("user1", "correct_password", true, "active", 0, false, false),
Arguments.of("user2", "wrong_password", true, "active", 1, false, false),
Arguments.of("user3", "correct_password", true, "locked", 0, false, false),
Arguments.of("user4", "correct_password", true, "active", 3, false, false),
Arguments.of("user5", "correct_password", true, "active", 2, true, false)
)
}

Conclusion

Parameterized tests can be very helpful for keeping code clean and simple, and for avoiding duplication. They should be used to test the same functionality with different input parameters to cover various scenarios. However, we should avoid using them if they become too complex and hard to understand.

Find out more about Life at Expedia Group.

--

--