Passing int array in ParameterizedTest in java - java

I am trying to pass in an array for testing a certain algorithm, but the arrays seem to not be passed correctly or at all. I manually tested the algorithm so I know it works as it's supposed to. How can I pass arrays in for testing in JUnit 5?
#ParameterizedTest
#CsvSource(value = {"[13,14,65,456,31,83],[1331,65456]"})
public void palindromeCombos(int[] input, int[] expected){
Palindrome pal = new Palindrome();
List<Integer> actual = pal.allPalindromes(input);
int[] result = new int[actual.size()];
for(int i = 0; i < actual.size(); i++){
result[i] = actual.get(i);
}
Assertions.assertArrayEquals(expected, result);
}

Pablo's Answer is correct, of course, but personally I'm not a fan of parsing strings if I don't absolutely have to. Another approach could be to use a MethodSource instead, and explicitly provide the arguments you need:
public static Stream<Arguments> palindromeCombos() {
return Stream.of(
Arguments.of(new int[]{13, 14, 65, 456, 31, 83}, new int[]{1331, 65456}));
}
#ParameterizedTest
#MethodSource
public void palindromeCombos(int[] input, int[] expected) {
// Test logic...
}

Since there is not implicit conversion for arrays, you can use explicit conversion, first you need to declare you converter class:
class IntArrayConverter implements ArgumentConverter {
#Override
public Object convert(Object source, ParameterContext context)
throws ArgumentConversionException {
if (!(source instanceof String)) {
throw new IllegalArgumentException(
"The argument should be a string: " + source);
}
try {
return Arrays.stream(((String) source).split(",")).mapToInt(Integer::parseInt).toArray();
} catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException("Failed to convert", e);
}
}
}
Then you can use it in your test:
#ParameterizedTest
#CsvSource(value = {
"13,14,65,456,31,83;1331,65456",
"1,2,3,4,5,6;10,20"}, delimiterString = ";")
public void palindromeCombos(#ConvertWith(IntArrayConverter.class) int[] input,
#ConvertWith(IntArrayConverter.class) int[] expected) {
System.out.println(Arrays.toString(input));
System.out.println(Arrays.toString(expected));
}
Notice that I removed the [] from the CsvSource and changed the delimiter to ;, so the arrays are expressed by a list of integers separated by comma.
If you want you can keep the format you had and handle it in the converter class.
For those two examples the output is:
[13, 14, 65, 456, 31, 83]
[1331, 65456]
[1, 2, 3, 4, 5, 6]
[10, 20]
If you need further information you can check this post: https://www.baeldung.com/parameterized-tests-junit-5

Related

is this a good way to call Junit assertions in a common method?

I have written tests which call a method to find the missing number in a given 1 to n array. The test code is as follows:
public class MissingNumberInArrayTest {
private int arrElements;
private int[] arr;
#Test
public void mustReturnMissingNumberCase1() {
// Given
arrElements = 5;
arr = new int[]{1, 2, 3, 4};
// When & Then
callAssertion(arr, arrElements, 5);
}
#Test
public void mustReturnMissingNumberCase2() {
// Given
arrElements = 5;
arr = new int[]{2, 3, 4, 5};
// When & Then
callAssertion(arr, arrElements, 1);
}
public void callAssertion(int[] arr, int arrElements, int expected) {
// When
int missingNumber = MissingNumberInArray.findMissingNumber(arr, arrElements);
// Then
Assertions.assertEquals(expected, missingNumber);
}
}
As you can see, I have multiple cases to test and have used the assertion in common method which takes input and asserts the data. Is this a good way of writing tests?
This question doesn't have an exact answer (IMO), so it will probably be deleted soon.
I wouldn't do it for simple cases like your example but if it makes your life easier, I don't see anything wrong with it. You could rename the method as assertMissingNumber and it wouldn't be that much different than usual (although the method you really want to test is in the assertion call).
I think your specific example is perfect for parameterized tests, though.
This is what it would look like with Junit 4:
#RunWith( Parameterized.class )
public class MissingNumberInArrayTest {
#Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList( new Object[][] {
{ 5, new int[]{1, 2, 3, 4}, 5},
{ 5, new int[]{2, 3, 4, 5}, 1}
});
}
#Parameterized.Parameter(0)
public int arrElements;
#Parameterized.Parameter(1)
public int[] arr;
#Parameterized.Parameter(2)
public int expected;
#Test
public void mustReturnMissingNumberCase() {
// When
int missingNumber = MissingNumberInArray.findMissingNumber(arr, arrElements);
// Then
assertEquals( expected, missingNumber);
}
}
You can check on the JUnit documentation for examples with JUnit 5

Beginner Java project: what is wrong with my Array?

I have just started learning Java as part of my university course and am having a problem with y first project. I am just starting to create a project that basically sorts coins. I am trying to make a method called printCoinList() that prints the contents of a coin list, indicating what denominations are currently in circulation (i.e "The current coin denominations are in circulation: 200,100,50,20,10), in pennies.
So far I have declared my instance fields, created a parameter and attempted to make this method. My only issue is when i try and test it in the main() method it seems to have a problem with me using an array as the coinList parameter. This is what I have so far:
public class CoinSorter {
//Instance Fields
String currency;
int minCoinIn;
int maxCoinIn;
int[] coinList;
//constructor
public CoinSorter(String Currency, int minValueToExchange, int maxValueToExchange, int[] initialCoinList) {
currency=Currency;
minCoinIn=minValueToExchange;
maxCoinIn = maxValueToExchange;
coinList= initialCoinList;
}
public void printCoinList() {
System.out.println("The current coin denominations are in circulation"
+ coinList);
}
public static void main(String[] args) {
//An example
CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, {10,20,50,100,200});
The only problems seems to be in exampleOne as when I take this out the rest of the code seems to run fine.
The error message is:
Exception in thread "main" java.lang.Error: Unresolved compilation problems:
The constructor CoinSorter(String, int, int, int, int, int, int, int) is undefined
Syntax error on token "{", delete this token
Syntax error on token "}", delete this token
So does anyone know what I am doing wrong?
This is because an array initializer may only be specified at declaration site or as part of an array creation expression. (JLS § 10.6)
Below is an array initializer at declaration site.
int[] array = { 2, 3, 5, 7, 11 };
This is short for
int[] array = new int[] { 2, 3, 5, 7, 11 };
However, it may not be used as 'array literal', unlike a string literal. That means that you must write out the array creation expression:
new CoinSorter("pounds", 0, 10000, new int[] { 10, 20, 50, 100, 200 });
Arrays in java can be declared/initialized using one of the following ways.
int[] myIntArray = {10,20,50,100,200};
int[] myIntArray = new int[]{10,20,50,100,200};
replace CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, {10,20,50,100,200});
with
CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, myIntArray );
OR
CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, new int[]{10,20,50,100,200});
Firt of all , in java , you need to specify the type of your Array :
CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, new int[]{10,20,50,100,200});
Then your "printCoinList" method will not work as excepted , this should print :
The current coin denominations are in circulation [I#7852e922
Your final code should be :
import java.util.Arrays;
public class CoinSorter {
//Instance Fields
String currency;
int minCoinIn;
int maxCoinIn;
int[] coinList;
//constructor
public CoinSorter(String Currency, int minValueToExchange, int maxValueToExchange, int[] initialCoinList) {
currency=Currency;
minCoinIn=minValueToExchange;
maxCoinIn = maxValueToExchange;
coinList= initialCoinList;
}
public void printCoinList() {
System.out.println("The current coin denominations are in circulation : "
+ Arrays.toString(coinList));
}
public static void main(String[] args) {
//An example
CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, new int[]{10,20,50,100,200});
exampleOne.printCoinList();
}
}
Result :
The current coin denominations are in circulation : [10, 20, 50, 100, 200]
Good luck :) I hope I could access to you future exchange and buy and hold some crypto :D
before creating an object CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, {10,20,50,100,200}); declare and initialize an array int arr[]={10,20,50,100,200}and then pass it in the constructor rather than passing {10,20,50,100,200}
like this
int arr[]={10,20,50,100,200};
CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, arr);

Junit test not working for sumoverarray method Java

I am trying to run a test for my sumoverarray method but I keep getting a failure saying AssertionError. Here's my method.
public class SumOverArray {
public static int IterateAndSum(int[] arr) {
int sum=0;
for (int i:arr) {
sum=sum+i;
}
return sum;
}
}
Here is my junit test
#Test
public void test3() {
int[] arr = { 1, 2, 3 };
assertNotEquals(0, SumOverArray.IterateAndSum(arr));
try {
SumOverArray.IterateAndSum(null);
assertTrue(true);
} catch (NullPointerException e) {
fail();
}
int [] arr2 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
assertEquals(78, SumOverArray.IterateAndSum(arr2));
int[] arr3 = { -1, -2, -3, -4, -5, -6, -7, -8, -9 };
assertEquals(-45, SumOverArray.IterateAndSum(arr3));
int[] arr4 = { 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 };
assertEquals(1095, SumOverArray.IterateAndSum(arr4));
}
The behaviour is expected. Looking at the second part of the test method:
try {
SumOverArray.IterateAndSum(null);
assertTrue(true);
} catch (NullPointerException e) {
fail();
}
We call SumOverArray.IterateAndSum with null and expect no NullPointerException to be thrown. But looking at the implementation, we do not perform a null-check in method SumOverArray.IterateAndSum and thus try to iterate over null. This, ultimately, leads to a NullPointerException to be thrown. The catch-block in the test is entered and the test fails.
All other tests would pass, if this test would pass.
Ideone demo
If we, however, want to assert that a NullPointerException is thrown when null is passed as parameter, then we can use Assertions.assertThrows(...):
final NullPointerException exception = assertThrows(
NullPointerException.class,
() -> SumOverArray.IterateAndSum(null));
// validate exception here if necessary
Some remarks on the code:
Method names in Java should always start with a lowercase letter (IterateAndSum -> iterateAndSum)
A test should test one thing and one thing only. Thus, the one test method above should be written as four tests.
The last three tests are redundant, one of them is sufficient.
The explicit fail() on an exception is superfluous and can be omitted.
Semantically, the first test is also covered by the last three tests. Thus this test is also redundant.
I would suggest to define a value to return if null is passed as argument, 0 seems sensible.
This leaves us with the following two tests:
#Test
#DisplayName("should return 0 if null is passed as argument")
public void shouldReturnZerofNullIsPassedAsParameter() {
// GIVEN: nothing
// WHEN
final int actual = SumOverArray.iterateAndSum(null);
// THEN
assertEquals(0, actual);
}
// Alternative test, if we want to assure that a NPE is thrown if null is passed
#Test
#DisplayName("should throw NPE if null is passed as argument")
public void shouldThrowNullPointerExceptionIfNullIsPassedAsParameter() {
// GIVEN: nothing
// WHEN & THEN
assertThrows(
NullPointerException.class,
() -> SumOverArray.iterateAndSum(null));
}
#Test
#DisplayName("should return 78 if 1, 2 , ... , 12 are summed")
void shouldReturnCorrectResult() {
// GIVEN
final int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
// WHEN
final int actual = SumOverArray.iterateAndSum(arr);
// THEN
assertEquals(78, actual);
}

How to define display name using nested attributes of arguments in ParameterizedTest(name = #{index} multiply {0[0]} x {0[1]} = {0[2]})

I cannot figure out if it is a missing feature, see the JUnit issue 1154 and my comment there, or just my inability to write properly the syntax for name of the #ParameterizedTest in JUnit5. Given the fact that issue has been open for last 3 years, I'm afraid I cannot wait till I get answer or even the implementation there, so I'm trying to ask here as well.
In my example ParameterizedTest, I'm using #MethodSource, which returns a Stream of ints arrays and I would like to use only those array's attributes instead of all {arguments} which is anyway same if I use {0}. Hard to explain verbally here, better to use code example bellow.
package net.delphym.unittests;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
class DynamicTestCreationTestParam {
private static Stream<int[]> data() {
return Stream.of(new int[][] { {1, 2, 2}, {5, 3, 15}, {121, 4, 484} });
}
#DisplayName("Multiplication test")
#ParameterizedTest(name = "#{index} for {arguments}: {0} x {1} = {2}")
#MethodSource("data")
void testWithStringParameter(int[] data) {
MyClass tester = new MyClass();
int m1 = data[0];
int m2 = data[1];
int expected = data[2];
assertEquals(expected, tester.multiply(m1, m2));
}
// class to be tested
class MyClass {
public int multiply(int i, int j) {
return i *j;
}
}
}
The test results output looks something like this for the 1st run: #1 for [1, 2, 2]: [1, 2, 2] x {1} = {2}
Ideally, I would like to see this: #1 multiply: 1 x 2 = 2.
So question is what would be the #ParametrizedTest(name) definition to match my expectation?
I was trying something like this:
name = "#{index} multiply : {[0][0]} x {[0][1]} = {[0][2]}"
But it is syntacticly incorrect.
Alright, as suggested by #Murat Karagöz, I'm only reposting suggestions which I receive on Github to have all at one place.
In my opinion, without no doubt, those elegant solutions are only workarounds to the JUnit5 #ParameterizedTest API limitations, but they both satisfied my needs.
Proposed workaround #1 if I don't need to use int[]
Solution presented here is preserving #MethodSource("data") and only altering its return type. Now the data() method returns Stream of Arguments objects (3 in each), instead Stream of int arrays (of 3 in each).
Then the code reads:
private static Stream<Arguments> data() {
return Stream.of(Arguments.of(1, 2, 2), Arguments.of(5, 3, 15), Arguments.of(121, 4, 484));
}
#DisplayName("Multiplication test")
#ParameterizedTest(name = "#{index}: {0} x {1} = {2}")
#MethodSource("data")
void testWithStringParameter(int m1, int m2, int expected) {
MyClass tester = new MyClass();
assertEquals(expected, tester.multiply(m1, m2));
}
Credits goes to Marc Philipp for his post # Github.
Proposed workaround #2 to unroll test data to match display needs##
This solution provided by sormarus suggest to use #CsvSource instead of the #MethodSource and provides the set of the test data within the annotation.
I like this as simple example how to quickly prepare same basic variation without a need to have a specific data generating method.
The code looks like this:
#DisplayName("Multiplication test")
#ParameterizedTest(name = "#{index} multiply: {0} x {1} = {2}")
#CsvSource({"1,2,2", "3,5,15", "121,4,484"})
void testWithStringParameter(int m1, int m2, int expected) {
MyClass tester = new MyClass();
assertEquals(expected, tester.multiply(m1, m2));
}

is there a Java equivalent to Javascript's "some" method?

I have a collection and I would like to know if at least one element meets some condition. Essentially, what some does in JavaScript, I would like to do on a collection!
As of Java 8, you can convert the Collection into a Stream and use anyMatch as in the following example.
import java.util.Arrays;
import java.util.List;
public class SomeExample {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, -6, 7);
boolean hasNegative = list.stream().anyMatch(x -> x < 0);
if (hasNegative) {
System.out.println("List contains some negative number");
}
else {
System.out.println("List does not contain any negative number");
}
}
}
Check out Guava's Iterables class and its any() implementation.
More or less the same thing as the Commons Collections example in the other answer, but genericized:
List<String> strings = Arrays.asList("ohai", "wat", "fuuuu", "kthxbai");
boolean well = Iterables.any(strings, new Predicate<String>() {
#Override public boolean apply(#Nullable String s) {
return s.equalsIgnoreCase("fuuuu");
}
});
System.out.printf("Do any match? %s%n", well ? "Yep" : "Nope");
You can use CollectionUtils from Apache commons-collections:
List<Integer> primes = Arrays.asList(3, 5, 7, 11, 13)
CollectionUtils.exists(primes, even); //false
Where even is a predicate:
Predicate even = new Predicate() {
public boolean evaluate(Object object) {
return ((Integer)object) % 2 == 0;
}
}
Or in an inlined version:
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13)
CollectionUtils.exists(primes, new Predicate() {
public boolean evaluate(Object object) {
return ((Integer)object) % 2 == 0;
}
});
Yes, it is ugly for two reasons:
Java does not (yet) support functions as first-class citizens, which are emulated with Single-Abstract-Method interface.
commons-collections does not support generics.
On the other hand in modern JVM languages like Scala you can write:
List(3,5,7,11,13,17).exists(_ % 2 == 0)
Java doesn't have this feature built-in. Javascript's some() accepts a function pointer as an argument, which is not something that's natively supported in Java. But it should be fairly straight forward to emulate the functionality of some() in Java using a loop and and an interface for the callback functionality.

Categories