If you want to learn how you can work smarter and save time when you are writing tests with JUnit 5, you should take a look at my Introduction to JUnit 5 course. It has 24 lessons, 47 exercises, and 13 quizzes.
This blog post describes how we can write parameterized tests with JUnit 5. After we have finished this blog post, we:
- Can get the required dependencies with Maven and Gradle.
- Know how we can customize the display name of each method invocation.
- Understand how we can use different argument sources.
- Can write custom argument converters.
Let's start by getting the required dependencies.
Getting the Required Dependencies
Before we can write parameterized tests with JUnit 5, we have to ensure that the junit-jupiter-params dependency is found from the classpath. If we are using the junit-jupiter aggregator artifact, we don't have to do anything because all the required dependencies are already added to the classpath. On the other hand, if we aren't using the junit-jupiter aggregator artifact, we have to make some changes to our build script.
If we are using Maven, we have to add the junit-jupiter-params dependency to the test scope. We can do this by adding the following snippet to dependencies section of our POM file:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
If we are using Gradle, we have to add the junit-jupiter-params dependency to the testImplementation dependency configuration. We can do this by adding the following snippet to our build.gradle file:
testImplementation(
'org.junit.jupiter:junit-jupiter-params:5.8.2'
)
Let’s move on and write our first parameterized test with JUnit 5.
Writing Our First Parameterized Tests
If our test method takes only one method parameter that's either a String or a primitive type supported by the @ValueSource annotation (byte, char, double, float, int, long, or short), we can write a parameterized test with JUnit 5 by following these steps:
- Add a new test method to our test class and ensure that this method takes a
Stringobject as a method parameter. - Configure the display name of the test method.
- Annotate the test method with the
@ParameterizedTestannotation. This annotation identifies parameterized test methods. - Provide the method parameters which are passed to our test method. Because our test method takes one
Stringobject as a method parameter, we can provide its method parameters by annotating our test method with the@ValueSourceannotation.
After we have added a new parameterized test to our test class, its source code looks as follows:
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@DisplayName("Pass the method parameters provided by the @ValueSource annotation")
class ValueSourceExampleTest {
@DisplayName("Should pass a non-null message to our test method")
@ParameterizedTest
@ValueSource(strings = {"Hello", "World"})
void shouldPassNonNullMessageAsMethodParameter(String message) {
assertNotNull(message);
}
}
When we run our parameterized test, we should see an output that looks as follows:
Pass the method parameters provided by the @ValueSource annotation |_ Should pass a non-null message to our test method |_ [1] Hello |_ [2] World
Even though this output looks quite clean, sometimes we want to provide our own display name for each method invocation. Let's find out how we can do it.
Customizing the Display Name of Each Method Invocation
We can customize the display name of each method invocation by setting the value of the @ParameterizedTest annotation's name attribute. This attribute supports the following placeholders:
{displayName}: The display name of the test method.{index}: The index of the current invocation. Note that the index of the first invocation is one.{arguments}: A comma separated list that contains all arguments passed to the test method.{argumentsWithNames}: A comma separated list that contains all arguments (including the name of the method parameter) passed to the test method.{i}: The actual method parameter (ispecifies the index of the method parameter). Note that the index of the first method parameter is zero.
Let's provide a custom display name to our test method. This display name must display the index of the current invocation and the provided method parameter. After we have configured the custom display name of each method invocation, the source code of our test class looks as follows:
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@DisplayName("Pass the method parameters provided by the @ValueSource annotation")
class ValueSourceExampleTest {
@DisplayName("Should pass a non-null message to our test method")
@ParameterizedTest(name = "{index} => message=''{0}''")
@ValueSource(strings = {"Hello", "World"})
void shouldPassNonNullMessageAsMethodParameter(String message) {
assertNotNull(message);
}
}
When we run our parameterized test, we should see an output that looks as follows:
Pass the method parameters provided by the @ValueSource annotation |_ Should pass a non-null message to our test method |_ 1 => message='Hello' |_ 2 => message='World'
As we remember, the @ValueSource annotation is a good choice if our test method takes only one method parameter that's supported by the @ValueSource annotation. However, most of the time this is not the case. Next, we will find out how we can solve this problem by using different argument sources.
Using Argument Sources
The @ValueSource annotation is the simplest argument source that's supported by JUnit 5. However, JUnit 5 support other argument sources as well. All supported argument sources are configured by using annotations found from the org.junit.jupiter.params.provider package.
This section describes how we can use the more complex argument sources provided by JUnit 5. Let's start by finding out how we can pass enum values to our parameterized test.
Passing Enum Values to Our Parameterized Test
If our parameterized test takes one enum value as a method parameter, we have to annotate our test method with the @EnumSource annotation and specify the enum values which are passed to our test method.
Let's assume that we have to write a parameterized test that takes a value of the Pet enum as a method parameter. The source code of the Pet enum looks as follows:
enum Pet {
CAT,
DOG;
}
If we want to pass all enum values to our test method, we have to annotate our test method with the @EnumSource annotation and specify the enum whose values are passed to our test method. After we have done this, the source code of our test class looks as follows:
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@DisplayName("Pass enum values to our test method")
class EnumSourceExampleTest {
@DisplayName("Should pass non-null enum values as method parameters")
@ParameterizedTest(name = "{index} => pet=''{0}''")
@EnumSource(Pet.class)
void shouldPassNonNullEnumValuesAsMethodParameter(Pet pet) {
assertNotNull(pet);
}
}
When we run this test method, we see that JUnit 5 passes all values of the Pet enum to our test method:
Pass enum values to our test method |_ Should pass non-null enum values as method parameters |_ 1 => pet='CAT' |_ 2 => pet='DOG'
If we want to specify the enum values that are passed to our test method, we can specify the enum values by setting the value of the @EnumSource annotation's names attribute. Let's ensure that the value: Pet.CAT is passed to our test method.
After we have specified the used enum value, the source code of our test class looks as follows:
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@DisplayName("Pass enum values to our test method")
class EnumSourceExampleTest {
@DisplayName("Should pass only the specified enum value as a method parameter")
@ParameterizedTest(name = "{index} => pet=''{0}''")
@EnumSource(value = Pet.class, names = {"CAT"})
void shouldPassNonNullEnumValueAsMethodParameter(Pet pet) {
assertNotNull(pet);
}
}
When we run this test method, we see that JUnit 5 passes only the value: Pet.CAT to our test method:
Pass enum values to our test method |_ Should pass non-null enum values as method parameters |_ 1 => pet='CAT'
@EnumSource annotation provides quite versatile support for filtering enum values. If you want to get more information about this, you should take a look at the following documents:
Additional Reading:
We have now learned how we can use two different argument sources that allow us to pass one method parameter to our test method. However, most of the time we want to pass multiple method parameters to our parameterized test. Next, we will find out how we can solve this problem by using the CSV format.
Creating Our Test Data by Using the CSV Format
If we have to pass multiple arguments to the invoked test method and the provided test data is used by only one test method (or a few test methods), we can configure our test data by using the @CsvSource annotation. When we add this annotation to a test method, we have to configure the test data by using an array of String objects. When we specify our test data, we have to follow these rules:
- One
Stringobject must contain all arguments of one method invocation. - The different argument values must be separated with a comma.
- The argument values found from each line must use the same order as the method parameters of our test method.
Let's configure the arguments which are passed to the sum() method. This method takes three method parameters: the first two method parameters contain two int values and the third method parameter specifies the expected sum of the provided int values.
After we have configured the test data of our parameterized test, the source code of our test class looks as follows:
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DisplayName("Should pass the method parameters provided by the @CsvSource annotation")
class CsvSourceExampleTest {
@DisplayName("Should calculate the correct sum")
@ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}")
@CsvSource({
"1, 1, 2",
"2, 3, 5"
})
void sum(int a, int b, int sum) {
assertEquals(sum, a + b);
}
}
Even though this looks quite clean, sometimes we have so much test data that it doesn't make sense to add it to our test class because our test class would become unreadable. Let's find out how we can load the test data that's passed to the sum() method from a CSV file.
Loading Our Test Data From a CSV File
We can load our test data from a CSV file by following these steps:
First, we have to create a CSV file that contains our test data and put this file to the classpath. When we add our test data to the created CSV file, we have to follow these rules:
- One line must contain all arguments of one method invocation.
- The different argument values must be separated with a comma.
- The argument values found from each line must use the same order as the method parameters of our test method.
The test-data.csv file configures the test data that is passed to the sum() method. This file is found from the src/test/resources directory, and its content looks as follows:
1,1,2 2,3,5 3,5,8
Second, we have to annotate our test method with the @CsvFileSource annotation and configure the location of our CSV file. After we have done this, the source code of our test class looks as follows:
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DisplayName("Should pass the method parameters provided by the test-data.csv file")
class CsvFileSourceExampleTest {
@DisplayName("Should calculate the correct sum")
@ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}")
@CsvFileSource(resources = "/test-data.csv")
void sum(int a, int b, int sum) {
assertEquals(sum, a + b);
}
}
We can now pass multiple method parameters to our parameterized test. However, the catch is that the method parameters of our parameterized tests must be supported by the DefaultArgumentConverter class. Its Javadoc states that:
The DefaultArgumentConverter is able to convert from strings to a number of primitive types and their corresponding wrapper types (Byte, Short, Integer, Long, Float, and Double), date and time types from the java.time package, and some additional common Java types such as File, BigDecimal, BigInteger, Currency, Locale, URI, URL, UUID, etc.
Next, we will find out how we can solve this problem by using a factory method and a custom ArgumentsProvider.
Creating Our Test Data by Using a Factory Method
If all parameterized tests which use the created test data are found from the same test class and the logic that creates the test data isn't "too complex", we should create our test data by using a factory method.
If we want to use this approach, we have to add a static factory method to our test class and implement this method by following these rules:
- The factory method must not take any method parameters.
- The factory method must return a
Stream,Iterable,Iterator, or an array ofArgumentsobjects. The object returned by our factory method contains the arguments of all test method invocations. - An
Argumentsobject must contain all arguments of a single test method invocation. - We can create a new
Argumentsobject by invoking thestatic of()method of theArgumentsinterface. The arguments provided to theof()method are passed to our test method when it's invoked by JUnit 5. That's why the provided arguments must use the same order as the method parameters of our test method.
- The factory method must be
staticonly if we use the default lifecycle configuration. - The factory method can also return a
Streamthat contain primitive types. This blog post doesn't use this approach because I wanted to demonstrate how we can pass "complex" objects to our parameterized tests.
Let's demonstrate these rules by implementing a factory method that creates the test data which is passed to the sum() method (we have already used this method in the previous examples). After we have implemented this factory method, the source code of our test class looks as follows:
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DisplayName("Should pass the method parameters provided by the sumProvider() method")
class MethodSourceExampleTest {
@DisplayName("Should calculate the correct sum")
@ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}")
void sum(int a, int b, int sum) {
assertEquals(sum, a + b);
}
private static Stream<Arguments> sumProvider() {
return Stream.of(
Arguments.of(1, 1, 2),
Arguments.of(2, 3, 5)
);
}
}
After we have implemented this method, we must ensure that its return value is used when JUnit 5 runs our parameterized test method. We can do this by following these steps:
- Annotate our test method with the
@MethodSourceannotation. - Configure the name of the factory method which creates our test data.
After we have made the required changes to our test class, its source code looks as follows:
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DisplayName("Should pass the method parameters provided by the sumProvider() method")
class MethodSourceExampleTest {
@DisplayName("Should calculate the correct sum")
@ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}")
@MethodSource("sumProvider")
void sum(int a, int b, int sum) {
assertEquals(sum, a + b);
}
private static Stream<Arguments> sumProvider() {
return Stream.of(
Arguments.of(1, 1, 2),
Arguments.of(2, 3, 5)
);
}
}
This approach works relatively well as long as the factory method is simple and all test methods which use the factory method are found from the same test class. If either of these conditions is false, we have to implement a custom ArgumentsProvider.
Creating Our Test Data by Using a Custom ArgumentsProvider
If the test methods which use our test data are found from different test classes or the logic which creates the required test data is so complex that we don't want to add it to our test class, we have to create a custom ArgumentsProvider.
We can do this by creating class that implements the ArgumentsProvider interface. After we have created this class, we have to implement the provideArguments() method that returns a Stream of Arguments objects. When we create the returned Stream object, we must follow these rules:
- The returned object must contain the arguments of all test method invocations.
- An
Argumentsobject must contain all arguments of a single test method invocation. - We can create a new
Argumentsobject by invoking thestatic of()method of theArgumentsinterface. The arguments provided to theof()method are passed to our test method when it's invoked by JUnit 5. That's why the provided arguments must use the same order as the method parameters of our test method.
Let's create a custom ArgumentsProvider which provides the test data that's passed to the sum() method. We can do this by following these steps:
First, we have write a custom ArgumentsProvider class which returns the test data that's passed to the sum() method.
After we have created a custom ArgumentsProvider class, the source code of our test class looks as follows:
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DisplayName("Should pass the method parameters provided by the CustomArgumentProvider class")
class ArgumentsSourceExampleTest {
@DisplayName("Should calculate the correct sum")
@ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}")
void sum(int a, int b, int sum) {
assertEquals(sum, a + b);
}
static class CustomArgumentProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception {
return Stream.of(
Arguments.of(1, 1, 2),
Arguments.of(2, 3, 5)
);
}
}
}
Second, we have to configure the used ArgumentsProvider by annotating our test method with the @ArgumentsSource annotation. After we have done this, the source code of our test class looks as follows:
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DisplayName("Should pass the method parameters provided by the CustomArgumentProvider class")
class ArgumentsSourceExampleTest {
@DisplayName("Should calculate the correct sum")
@ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}")
@ArgumentsSource(CustomArgumentProvider.class)
void sum(int a, int b, int sum) {
assertEquals(sum, a + b);
}
static class CustomArgumentProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception {
return Stream.of(
Arguments.of(1, 1, 2),
Arguments.of(2, 3, 5)
);
}
}
}
We can now create our test data by using factory methods and custom ArgumentsProvider classes. However, even though these methods allow us to ignore the limitations of the DefaultArgumentConverter class, sometimes we want to provide our test data by using strings because this helps us to write tests which are easier to read than tests which use factory methods or custom ArgumentsProvider classes.
Next, we will find out how we can solve this problem by using a custom ArgumentConverter.
Using a Custom ArgumentConverter
An ArgumentConverter has only one responsibility: it converts the source object into an instance of another type. If the conversion fails, it must throw an ArgumentConversionException.
Let's create an ArgumentConverter that can convert a String object into a Message object. The Message class is a simple wrapper class that simply wraps the message given as a constructor argument. Its source code looks as follows:
final class Message {
private final String message;
Message(String message) {
this.message = message;
}
String getMessage() {
return message;
}
}
We can create our custom ArgumentConverter by following these steps:
First, we have to create a class called MessageConverter that implements the ArgumentConverter interface. After we have created this class, its source code looks as follows:
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.params.converter.ArgumentConversionException;
import org.junit.jupiter.params.converter.ArgumentConverter;
final class MessageConverter implements ArgumentConverter {
@Override
public Object convert(Object source, ParameterContext context) throws ArgumentConversionException {
}
}
Second, we have to implement the convert() method by following these steps:
- Throw a new
ArgumentConversionExceptionif the source object isn't valid. The source object must be aStringthat's notnullor empty. - Create a new
Messageobject and return the created object.
After we have implemented the convert() method, the source code of the MessageConverter class looks as follows:
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.params.converter.ArgumentConversionException;
import org.junit.jupiter.params.converter.ArgumentConverter;
final class MessageConverter implements ArgumentConverter {
@Override
public Object convert(Object source, ParameterContext context) throws ArgumentConversionException {
checkSource(source);
String sourceString = (String) source;
return new Message(sourceString);
}
private void checkSource(Object source) {
if (source == null) {
throw new ArgumentConversionException("Cannot convert null source object");
}
if (!source.getClass().equals(String.class)) {
throw new ArgumentConversionException(
"Cannot convert source object because it's not a string"
);
}
String sourceString = (String) source;
if (sourceString.trim().isEmpty()) {
throw new ArgumentConversionException(
"Cannot convert an empty source string"
);
}
}
}
After we have created our custom ArgumentConverter, we have to create a parameterized test which uses our custom ArgumentConverter. We can create this test by following these steps:
First, we have to create a new parameterized test method by following these steps:
- Add a new parameterized test method to our test class and ensure that the method takes two
Messageobjects as method parameters. - Annotate the test method with the
@CsvSourceannotation and configure the test data by using the CSV format. - Verify that the
Messageobjects given as method parameters contain the same message.
After we have created our test method, the source code of our test class looks as follows:
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DisplayName("Pass converted Message objects to our test method")
class MessageConverterExampleTest {
@DisplayName("Should pass same messages as method parameters")
@ParameterizedTest(name = "{index} => actual={0}, expected={1}")
@CsvSource({
"Hello, Hello",
"Hi, Hi",
})
void shouldPassMessages(Message actual, Message expected) {
assertEquals(expected.getMessage(), actual.getMessage());
}
}
Second, we have to configure the ArgumentConverter that creates the arguments passed to our test method. We can do this by annotating the method parameters with the @ConvertWith annotation. When we do this, we have to configure the used ArgumentConverter by setting the value of the @ConvertWith annotation's value attribute.
After we have configured the used ArgumentConverter, the source code of our test class looks as follows:
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.converter.ConvertWith;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DisplayName("Pass converted Message objects to our test method")
class MessageConverterExampleTest {
@DisplayName("Should pass same messages as method parameters")
@ParameterizedTest(name = "{index} => actual={0}, expected={1}")
@CsvSource({
"Hello, Hello",
"Hi, Hi",
})
void shouldPassMessages(@ConvertWith(MessageConverter.class) Message actual,
@ConvertWith(MessageConverter.class) Message expected) {
assertEquals(expected.getMessage(), actual.getMessage());
}
}
Additional Reading:
We can now write parameterized tests with JUnit 5. Let's summarize what we learned from this blog post.
Summary
This blog post has taught us seven things:
- Before we can write parameterized tests with JUnit 5, we have to ensure that the
junit-jupiter-paramsdependency is found from the classpath. - We have to annotate our parameterized test method with the
@ParameterizedTestannotation. - We can customize the display name of each method invocation by setting the value of the
@ParameterizedTestannotation'snameattribute. - When we configure our test data, our test data must use the same order as the method parameters of our test method.
- If we want pass "complex" objects to parameterized tests which are found from the same test class and the logic that creates these arguments isn't “too complex”, we should create these arguments by using a factory method.
- If the test methods which use our test data are found from different test classes or the logic which creates the required test data is so complex that we don’t want to add it to our test class, we have to create a custom
ArgumentsProvider. - If we want to provide our test data by using strings and use method parameters which aren't supported by the default argument converters, we have to implement a custom
ArgumentConverter.
P.S. You can get the example application of this blog post from Github.
Hi Petri
Thanks by this valuable tutorial.
I have the following in JUnit 4, what is your best recommendation about if is necessary to adapt it to JUnit 5 (perhaps is not necessary)
@Parameters(name="{index}") public static Collection data() throws URISyntaxException { .... } public SomeTestClass(Class1 a, ..., ClassN n){ this.a = a; ... this.n = n; }From above some points:
@Parametersdata()method must have no parameters.data()method.Something interesting in your tutorial that perhaps should be highlighted, the are no
@Testmethods.Thanks
-Manuel
Hi Manuel,
I think that migrating your existing tests to use JUnit 5 is not a good idea unless:
The thing is that changing your existing code is basically an investment and you want to ensure that you get a good return on your investment. The "problem" is that it's very hard to get even a decent ROI if your existing tests don't have any problems AND the conditions I mentioned earlier are false.
In other words, I guess the answer to your question is: it depends, but most of the time you shouldn't do it.
Hi Petri,
Thank you for the documentation. Its quite elaborate.
I am migrating all my tests from junit 4 to 5.
I am writing a @ParameterizedTest with @MethodSource as input parameters provider.
But the @MethodSource factory method I use has to get parameters from different methods as per the current tests that I have.
Example:
public String domain= registrationPage.getFullDomain(); public String email= registrationPage.getEmail(); public String password= registrationPage.getPassword(); Stream registrationInputParameters() { return Stream.of( Arguments.of(domain, email, password) ); } @ParameterizedTest() @MethodSource("registrationInputParameters") public void sampleTest(String domain, String email, String password) { RegistrationPage registrationPage= open(fullDomain); registrationPage.register(email, password); }These values are throwing nullpointer exceptions and as per your documentation about the @MethodSource factory method 'A factory method must not take any method parameters'. Can you please let me know if the above functionality can be achieved any way using any other argument sources provided by @ParameterizedTest in junit 5
Hi,
can you let me know what your use case is? It's clear that you want to pass domain, email address, and password to your parameterized test method, but I am not sure how do you specify your test data. For example, do you want to read it from an external source (such as a file or a database) or do you want use a "constant dataset" (a set of constant values)?
Thank you for replying.
I want the test data from few base pages. Like I have a loginPage for all the login related data . In the above example I got the domain, email and password from the base registrationPage. This data from the loginPage or registrationPage or a different page will go into the @MethodSource as argument parameters and from the @MethodSource to the ParameterizedTest.
I have a RegistrationPage in which I have the getFullDomain()
This BasePage below has the RegistrationPage object created and @Autowired so I can access the RegistrationPage getFullDomain() method using the BasePage
My ParameterizedTest Class below is extending the BasePage so I can use the methods in registrationPage without creating an instance for the class separately in the test below.
When I do this and access the getFullDomain() method in my @MethodSource it returns null. And your document on the @MethodSource above says we can't pass external parameters into the @MethodSource. Just wondering if we have any solution for it.
Thank you.
Hi,
I noticed two things:
RegistrationPagebean into theBasePagebean by using field injection. However, theBasePageclass isn't annotated with a bean annotation. Also, your test class doesn't load the Spring application context. If you use this configuration, the value of theregistrationPagefield isnullwhen you run your test.registrationInputParameters()must bestatic.how can we pass string formatted using String.format() as value
I want to do for the json file similar to display in above examples for csv, but in my case I do have multiple json having different keys in each json. I want to extract single key example: key1 from each json and display that key value to parameterisedTest annotation dynamically.
@ParameterizedTest
@JsonFileSource(resource="file1.json")
public void sampleTestMethod(JsonObject obj){
}
}
inside file1.json , I do have multiple jsons and I want to extract key1 from each json and display that key value to the @ParameterizedTest dynamically to show test case name at run time with that value.(key1 holding test case name)
Hi,
First, I am sorry that it took me a long time to answer to your question. I just get so many comments nowadays that sometimes I miss one or two.
Second, unfortunately I don't know how you can fulfill your requirement. I hope that you managed to find an answer to your problem though.
Hi,
You can use this dependency:
http://www.joshka.net/junit-json-params/
Regards!
Everson Mauda (www.mauda.com.br)
Do we have a solution for it? I am facing the same requirement.
Hello Petri
I am so glad I found your tutorial, and really appreciate the references to the documentation. You also covered various ways of Parameterized testing with enough detail and explanation that I can follow. Thank you. I have a question. I want to create and populate a tree with data, and then test methods that use the populated tree to get the answer. In order to do this, I need to create two other object types: Point2D, and RectHv, and I like to be able to create different number of each and different instances for different tests.
So far I have looked into using the @TestInstance(TestInstance.Lifecycle.PER_CLASS) attribute as well as different ParameterizedTesting annotation tags but have not had any success. I am working on the following factory method but have not figured out how to convert the stream I have to one of type . I also get a compiler message saying "the stream is of type void"; my stream that is. The one I created. Here is what I have ...
I am still reading about streams, and need to read the links you provided above. Again, do not know how to thank you for creating this content, and taking the time to provide the links. Before I found your site, I was thinking I wish the articles on Parametrized Testing provided a bit more details and / or covered other supported features. Most were a bit over what I could follow. thank you so much.
I wanted to post an update since I have made progress, but run into an error stating "No ParameterResolver registered for parameter [edu.princeton.cs.algs4.RectHV arg1] in method [void KdTreeTest.range(KdTree,edu.princeton.cs.algs4.RectHV,edu.princeton.cs.algs4.Point2D[])]." I implmented an AgumentProvider as follows:
Hi,
First, I am sorry that it took me some time before I noticed your comment.
The problem is found from the
provideArguments()method of theKdTreeArgumentsProviderclass.When you invoke the
of()method of theStreamclass, you create a stream which has three elements (kt,r, andexpectedPoints). Also, because you use themap()method of theStreamclass, each element is wrapped inside anArgumentsobject. In other words, the returned stream contains threeArgumentsobjects which have one argument per object. Because your parameterized test has three arguments and your provider provides only one, your test fails.You can fix this problem by changing the implementation of the
provideArguments()method:I hope that this solves your problem.
Hello and thank you so much for the reply. I'll try the solution.