Soft Asserts – Why should you use them for Unit and Integration tests?
March 27, 2023JUnit 5 – When to use Internal MethodSource
July 10, 2023Introduction
This article belongs to the Managing Test Data series.
I will show you different ways to smartly use testing framework features when the date changes but the test output is the same.
In this article, you will see one use case for the Value Source feature.
Value Source
The Value Source feature is an annotation to use for the data-driven tests or Parameterized Tests in JUnit 5 terminology.
It lets you specify values as a single argument (method parameter) in your test.
The syntax is:
@ValueSource(literalValues = { values, separated, by, comma })
The literalValues
is one of the supported literal values: short
, byte
, int
, long
, float
, double
, char
, boolean
, java.lang.String
and java.lang.Class
.
The { values, separated, by, comma }
means that you need to specify the values separated by a comma.
Note: I cannot see a valid use case for the class parameter. Maybe only in a super specific test where you need to test the extensibility of a class or any other possible architectural test.
The best application of Value Source
The Boundary Value Analysis is a testing technique where we verify the extreme ends of the boundaries between partitions of the input value.
One of the most classic examples is the range of a given age restriction. Let’s assume we have a system that accepts, as inputs, an age between 18 and 30, where the age in this group will return a successful message and the ages outside this group will return an error message.
The following illustration shows the boundaries and the possible values.
Invalid (min -1) | Valid (min, min +1, max -1, max) | Invalid (max + 1) |
---|---|---|
17 | 18, 19, 29, 30 | 31 |
Note that we are playing with the extreme values, meaning adding or subtracting 1 (one) from the data requirement.
Below you can find a (silly) example of the code implementation: when the age is between 18 and 30 (both inclusive) the return is true, otherwise, it’s false
public class Example {
private Boolean isAgeValid(int age) {
return age >= 18 && age <= 30 ? Boolean.TRUE : Boolean.FALSE;
}
}
This technique exists for a reason: to test the boundaries that, in this case, are the equals or greater than (=>
) and the equals or less than (<=
). It’s a simple example but anyone, in a professional environment, could change their values thinking that the logic is wrong.
This is an excellent example of the Value Source feature, where we can test both valid and invalid scenarios. The test would look like this:
class AgeTest {
@DisplayName("Valid ages")
@ParameterizedTest(name = "{0} is a valid age for the requirement 18 to 30")
@ValueSource(ints = {18, 19, 29, 30})
void validAges(int age) {
Assertions.assertThat(isAgeValid(age)).isTrue();
}
@DisplayName("Invalid ages")
@ParameterizedTest(name = "{0} is an invalid age for the requirement between 18 to 30")
@ValueSource(ints = {17, 31})
void invalidAges(int age) {
Assertions.assertThat(isAgeValid(age)).isFalse();
}
}
In the validAges
test, line 6, you can see the usage of the valid data, on line 6, in the @ValueSource
annotation, same for the invalidAges
test in line 14.
The test result, in your IDE, will look like this:
✔ AgeTest
└─ ✔ Valid Ages
├─ ✔ 18 is a valid age for the requirement 18 to 30
├─ ✔ 19 is a valid age for the requirement 18 to 30
├─ ✔ 29 is a valid age for the requirement 18 to 30
└─ ✔ 30 is a valid age for the requirement 18 to 30
└─ ✔ Invalid Ages
├─ ✔ 17 is an invalid age for the requirement 18 to 30
└─ ✔ 31 is an invalid age for the requirement 18 to 30
Note that you will see an output similar to this because the @DisplayNam
e annotation shows the test name and the @ParameterizedTest
annotation gives it a name
, using the value parameters on it.
Are there more applications for the Value Source?
Another good example is the test of the negative or edge cases for Strings when you need to verify if the null, empty, space, etc.. (related to a String) will or won’t work.
This is already covered in the Null and Empty sources JUnit 5 documentation.
All the other possible usages, from my perspective, will be better using the other data-driven features, but if you know more use cases for the Value Source, leave a comment.
When should I use the Value Source?
- when you have a single data entry
- when it’s possible to apply the boundary value analysis
What are the benefits of using the Value Source?
- readability: you will understand the data required without the necessity to go to any other class or method to see its data
- helps you to test self-isolated methods, meaning a great choice for unit tests