In the previous chapter, we saw how we can pass one parameter to a test method by using an annotation from JUnit5.
How about if we need to pass in several parameters, which are also constant values as we had in the previous chapter?
For such a situation, we could use the @CsvSource
annotation from JUnit5, of course, coupled with the @ParameterizedTest
annotation.
Let's take a look at a few examples of how to use @CsvSource
.
Let's first create a method, which will take two parameters, both of them being strings.
I will create a method called csvSource_StringString
, so that we know what parameters we want this method to take.
I will also specify the two parameters - String param1, String param2
.
Of course, in your tests, you need to give all the methods and parameters names relevant to their purpose.
However, here, because I'm just showing examples, I will just say param1
and param2
.
What do we want to do with these values?
Well, just as before, we will just print them out to the console, so we see what those values were.
Now, as I mentioned, the annotation we will be using here is also used in conjunction with the @ParameterizedTest
one, so we will first specify that one.
@ParameterizedTest
void csvSource_StringString(String param1, String param2) {
System.out.println("param1 = " + param1 + ", param2 = " + param2);
}
Then we will go ahead and specify that we want to use the @CsvSource
annotation.
Here, we will need to provide the values that we need in our tests.
We will type value =
, and we will need to start writing them as pairs - in our case, contained by double quotes.
So, if we want to have, let's say, three pairs of parameters here, we will just have to put each pair between ""
, just as I'm showing you right now.
Let's say we want to send names of - let's say, superheroes - and let's start with specifying the name of our favorite hero, Captain America - namely "steve,rogers".
As you can see, I wrote two strings here and I separated them by comma.
That is because we are now using the @CsvSource
annotation, which means comma separated values.
So here, all of the parameters that we will pass will be separated by commas.
In our case, because we have two parameters, we will have the first string, then the comma, and then the second string, and everything will be in double quotes.
Let's take a look at our second superhero - let's call this one "captain,marvel".
Then for the third hero, let's go with "bucky,barnes".
So, at this point we have specified the annotation and we have also specified the values.
@ParameterizedTest
@CsvSource(value = {"steve,rogers", "captain,marvel", "bucky,barnes"})
void csvSource_StringString(String param1, String param2) {
System.out.println("param1 = " + param1 + ", param2 = " + param2);
}
So, three pairs of two values, and as you can see, the param1
values will be "steve", "captain", and "bucky".
Let's run the test to see the output in the console.
When I run the test, on the left-hand side, we will again see that we are running the csvSource_StringString
method, and we will see the values that were used for each test run, just as the case with the previous annotation that we used - namely the @ValueSource
.
For the first run, we use "steve" and "rogers" as the two parameter values.
You can also see on the right-hand side, that param1
was "steve" and param2
was "rogers", just as we expected.
In this case, we had three test runs of the same method and in each test run, the parameters were taken from the array of parameters that were each specified between double quotes.
For this method, we had two strings as parameters.
Let's see if we can specify something else too.
I will start with @ParameterizedTest
annotation, and then I will also write the @CsvSource
annotation.
In this example, I want to use a string
, an int
, and a boolean
as the parameters.
So, I will just create the method definition and I will say csvSource_StringIntBoolean
, just so I show you different values that you can pass into your methods.
So I would create the method signature by saying that the param1
type is String
, the param2
type is int
, and the param3
type is boolean
.
@ParameterizedTest
void csvSource_StringIntBoolean(String param1, int param2, boolean param3) {
System.out.println("param1 = " + param1 + ", param2 = " + param2 + ", param3 = " + param3);
}
Of course, in the test, I will, again, only output the values of these parameters and I will go back to the @CsvSource
annotation to start specifying the values.
I will have to say, value =
, and again, between curly brackets, I will specify the sets of parameters.
Let's start with the first set.
I will say again, "steve", I will pass in a random number here - "32", and then I will say "true".
For the second set, I will say "captain,21,false".
Again, these are just example values.
For the third set of parameters, I will say "bucky,5,true".
You might have noticed that when we define the values, everything is between the double quotes and we don't have an explicit way of specifying the type of each of these values here.
However, in the method definition, we will specify the type of each param and the annotation will wire them together.
So it will know that the first parameter value is a String
, the second parameter value is an int
, and the third one is a boolean
, and this is valid for all the sets of parameters that we are passing here.
@ParameterizedTest
@CsvSource(value = {"steve,32,true","captain,21,false","bucky,5,true"})
void csvSource_StringIntBoolean(String param1, int param2, boolean param3) {
System.out.println("param1 = " + param1 + ", param2 = " + param2 + ", param3 = " + param3);
}
Let's run the test and see the result.
As you can see on the left-hand side, again, we have the name of the method, and then we have all of the parameter values that we had passed to the test.
The console output on the right-hand side also shows us that, for example, for the third set of parameters, the String
param was "bucky", the int
param was "5", and the boolean
param was "true".
So you've seen that using @CsvSource
, we are able to specify a larger number of parameters - larger than one, to separate the parameter values.
What happens if we have, let's say, only strings and some of the strings do contain the comma character?
For such a situation, we do want the comma to be a part of the string itself, so we don't want to consider it as a delimiter.
We will need to escape the comma when it comes to those strings that contain the comma.
In order to do that, let's go and create a new @ParameterizedTest
with a @CsvSource
annotation, and let's specify the values.
Let's just say that, for example, we want to set the parameters as being a "nickname," and a "firstname, lastname", but we want this to only represent two parameters.
In order to do that, we can start by creating the first pair.
So we will say that we want the value of the first parameter to be "captain america", but we want the second value, so the value of the second parameter, to be "steve,rogers".
We don't want three or four parameters here - we only want two.
In that case, we would need to use simple quotes ''
and we will say 'steve,rogers'.
So here, we have actually created two parameter values instead of three, although we have the comma twice.
@ParameterizedTest
@CsvSource(value = {"captain america,'steve,rogers'"})
Let's continue with another value here.
Let's say we want "winter soldier", and we want this to be none other than 'bucky,barnes'.
Let's create the method that takes these parameters and we will say csvSource_StringWithComma
.
We will then say String param1, String param2
.
Again, our test will only output the values of these parameters to the console.
@ParameterizedTest
@CsvSource(value = {"captain america,'steve,rogers'", "winter soldier,'bucky,barnes'"})
void csvSource_StringWithComma(String param1, String param2) {
System.out.println("param1 = " + param1 + ", param2 = " + param2);
}
Now let's run this test and see the output, where we can see that the first parameter will be the so-called alias of our superheroes, which are "captain america" and "winter soldier", whereas the second parameter will be the full name, separated by a comma.
What about the situations where we just don't want the comma to be our delimiter?
What if, for some reason, we need a different delimiter instead of a comma?
We can do that by specifying the delimiter as an attribute to the @CsvSource
annotation.
I will create a new parameterized test, which takes a @CsvSource
, and we will just provide the values as strings here, and we will just provide the first name and last name of our superheroes just as in the first example.
So we will say "steve rogers", but in this case - our separator - we want it to be something different.
So let's say that instead of a comma, we want to use a question mark.
Now for the second pair, we will write "bucky?barnes".
And then in the @CsvSource
annotation, we will have to specify the delimiter attribute.
This one is the question mark character - so it's a char, it's not a string.
We can only use a single character as the separator here.
@ParameterizedTest
@CsvSource(value = {"steve?rogers", "bucky?barnes"}, delimiter = '?')
We will then create the method csvSource_StringWithDiffDelimiter
.
In this case, again, we have two strings as parameters. So param1
and param2
, and we will just output the values of the parameters to the console.
@ParameterizedTest
@CsvSource(value = {"steve?rogers", "bucky?barnes"}, delimiter = '?')
void csvSource_StringWithDiffDelimiter(String param1, String param2) {
System.out.println("param1 = " + param1 + ", param2 = " + param2);
}
Now let's see what happens when we run this test.
You will see that in the console, the values were properly displayed, meaning that even though we used the question mark as the delimiter, JUnit managed to see that we had "steve" and "rogers" as the parameter values.
You also can see on the right-hand side that the output of the test is correct.
This concludes our chapter on @CsvSource
, so this is a very useful annotation when we want to pass several constant values to our tests.