Reputation: 1908
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.
Upvotes: 20
Views: 10920
Reputation: 111
Array:
static Stream<Arguments> yourTest () {
return Stream.of(
Arguments.of((new int[] { 2, 1, 2, 3, 4 }), 3),
Arguments.of((new int[] { 2, 2, 0 }), 3),
Arguments.of((new int[]{1, 3, 5} ) ,0 )
);
}
// if not Array : List : Arguments.of((Arrays.asList(0, 1) ), 0.5)...
@ParameterizedTest(name = "{index} => array = {0} ), expected = {1} ")
@MethodSource("yourTest")
void shoultCountEvens( int[] array, int expected) {
assertEquals( CountEvens.countEvens(array), expected );
}
public class CountEvens {
public static int countEvens(int[] nums) {
long count = Arrays.stream(nums)
.filter(a -> a % 2 == 0)
.count();
return Math.toIntExact(count);
}}
Upvotes: 2
Reputation: 5592
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.
Upvotes: 9
Reputation: 109
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.
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();
}
}
Upvotes: 9
Reputation: 2610
I like using Spock for testing Java code. It's a groovy-based test framework that sits on top of JUnit 4. Parameterized tests in Spock are a built-in feature:
def "The reverseQuote method doesn't return null"(double[] qsp) {
when: "reverseQuote is called"
double[] rev = reverseQuote(qsp)
then: "the result is not null"
null != rev
where: "there are various input values"
qsp << [
[0.1, 0.2, 0.3] as double[],
[1.0, 2.0, 3.0] as double[]
]
}
... alternatively, you can lay out your test data in tabular form:
def "The reverseQuote method reverses the input array"(List qsp, List expected) {
when: "reverseQuote is called"
double[] rev = reverseQuote(qsp as double[])
then: "the result is the reverse of the input"
expected as double[] == rev
where: "there are various input values"
qsp | expected
[0.1, 0.2, 0.3] | [0.3, 0.2, 0.1]
[1.0, 2.0, 3.0] | [3.0, 2.0, 1.0]
}
Note that the as double[]
prevalence is an unfortunate consequence of Groovy automatically converting arrays to Lists, so we have to explicitly cast them back in the particular cases where we are interacting with Java code that actually requires an array.
Upvotes: 5
Reputation: 104
The JUnit documentation suggests to use @MethodSource
@ParameterizedTest
@MethodSource("range")
void testWithRangeMethodSource(double argument) {
assertNotEquals(9.0, argument);
}
static DoubleStream range() {
return DoubleStream.range(0.0, 20.0);
}
Otherwise you could consider using this: https://junit.org/junit5/docs/5.0.2/api/org/junit/jupiter/params/provider/ValueSource.html#doubles
@ParameterizedTest
@ValueSource(doubles = { 1, 2, 3 })
public void test(double numberToTest){
//do whatever
}
Upvotes: -1
Reputation: 4316
You can check TestNg (I'm using it in my project). Example in my project you can check here enter link description here or below:
@DataProvider(name = "processText")
public Object[][] dataProvider() {
return new Object[][]{
new Object[]{"ala\nma\nkota", "grep ma", "ma"},
new Object[]{"ala\nma\nkota", "grep -v ma", "ala\nkota"},
new Object[]{"ala\nma\nkota", "cut -c1-3", "ala\nma\nkot"},
//...
new Object[]{"ala ma kota", "sed s/ma/XX/g", "ala XX kota"},
new Object[]{"ala\nma\nkota", "grep -v ma | sed s/a/G/g", "GlG\nkotG"},
};
}
@Test(dataProvider = "processText")
public void testProcessText(String text, String cli, String expected) {
final String actual = new UnixProcessing().processText(text, cli);
assertEquals(actual, expected);
}
Official TestNg documentation is here
Upvotes: -2