I'm trying to parameterize this test:
@Test
public void reverseQuote(double[] qsp) throws Exception {
...}
It seems absurd to me that it doesn't exists some quick method to initialize array qsp
like, for example, ValueSource
:
@ParameterizedTest
@ValueSource(ints = { 1, 2, 3 })
void testWithValueSource(int argument) {
assertNotNull(argument);
}
my aim is to do something like @ValueSource(doublesArray = {new double[]{1.0, 2.0, 3.0}})
(that now returns error). Doesn't exists anything that permits something similar??
Other answers seem to suggest only elaborated ways, like using @MethodSource
or @ConvertWith
.
I accept answers implementing other testing libraries, too.
JUnit 5 provides a different approach. It provides the @BeforeAll annotation which is used on a static function, to work with static members of the class.
JUnit 4 has introduced a new feature called parameterized tests. Parameterized tests allow a developer to run the same test over and over again using different values. There are five steps that you need to follow to create a parameterized test. Annotate test class with @RunWith(Parameterized. class).
Ok, this is gonna be a weird answer, but it works and it was kinda fun to do.
First thing: your way is impossible. Not because of JUnit or any related API, but because of Java - valid annotation type elements (annotation arguments can only be primitive, String, Class, Enum, other annotation and array of all those).
Second thing: we can get around the first one. Check this:
@ArraySources(
arrays = {
@ArraySource(array = {1, 2, 3}),
@ArraySource(array = {4, 5, 6}),
@ArraySource(array = {7, 8, 9})
}
)
As it says, annotation can have other annotations as arguments, and arrays of those, so we are using those 2 rules here.
Third thing: how does that help? We can add our own annotation + argument provider, JUnit 5 is expansible in that way.
Both annotations:
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(ArrayArgumentsProvider.class)
public @interface ArraySources {
ArraySource[] arrays();
}
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ArraySource {
int[] array() default {};
}
Argument provider based on the annotations:
public class ArrayArgumentsProvider implements ArgumentsProvider, AnnotationConsumer<ArraySources> {
private List<int[]> arguments;
public void accept(ArraySources source) {
List<ArraySource> arrays = Arrays.asList(source.arrays());
this.arguments = arrays.stream().map(ArraySource::array).collect(Collectors.toList());
}
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return this.arguments.stream().map(Arguments::of);
}
}
And the final test using those:
public class ArraySourcesTest {
@ParameterizedTest
@ArraySources(
arrays = {
@ArraySource(array = {1, 2, 3}),
@ArraySource(array = {4, 5, 6}),
@ArraySource(array = {7, 8, 9})
}
)
void example(int[] array) {
System.out.println(Arrays.toString(array));
System.out.println("Test Over");
}
}
/* Output
[1, 2, 3]
Test Over
[4, 5, 6]
Test Over
[7, 8, 9]
Test Over
*/
You mentioned @MethodSource
as complicated, well, so I think I failed in this matter, but it works. It could be simplified and enhanced obviously (like naming annotation arguments as defaults - value - and I only did it for int
to show the idea). Not sure if you could achieve the same with existing features (ArgumentsProvider
and ArgumentSources
), but this looks more specific (you know you are working with arrays) and shows possibilities of extending JUnit5, may be useful in other case.
Using a combination of Junit Parameterized Tests and YAML Parsing might be something to consider.
@RunWith(Parameterized.class)
public class AnotherParameterizedTest {
private final HashMap row;
@Parameterized.Parameters(name="Reverse Lists Tests # {index}:")
public static List<Map<String, Object>> data() {
final TestData testData = new TestData(""+
"| ID | List | Expected | \n"+
"| 0 | [1, 2, 3] | [3, 2, 1] | \n"+
"| 1 | [2, 3, 5] | [3, 2, 1] | \n"+
"| 2 | [5, 6, 7] | [ 7, 6, 5] | \n"
);
// parsing each row using simple YAML parser and create map per row
return testData.getDataTable();
}
// Each row from the stringified table above will be
// split into key=value pairs where the value are parsed using a
// yaml parser. this way, values can be pretty much any yaml type
// like a list of integers in this case.
public AnotherParameterizedTest(HashMap obj) {
this.row = obj;
}
@Test
public void test() throws Exception {
List orgListReversed = new ArrayList((List) row.get("List"));
Collections.reverse(orgListReversed);
assertEquals((List) row.get("Expected"), orgListReversed);
}
}
Instead of using a String I am using a Excel Reader to do the same with simple Excel Tables. Parsing each row into one Map using YAML for the Values.
Junit IDE Test Results
The same just tested using Junit Jupiter gives nicer results in the IDE Runner.
import static org.junit.jupiter.api.Assertions.assertEquals;
import de.deicon.yatf.runner.dsl.TestData;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class FirstTest {
@ParameterizedTest
@MethodSource("testTable")
public void test(Map row){
List reversedList = (List) row.get("List");
Collections.reverse(reversedList);
assertEquals((List)row.get("Expected"), reversedList);
}
static List<Map<String, Object>> testTable() {
return new TestData(""+
"|ID| List |Expected | \n"+
"|0 | [1,2,3] | [3,2,1] | \n"+
"|1 | [hans, peter, klaus] | [klaus, peter, hans] | \n"
).getDataTable();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With