Parameterized test using custom objects - java

I am trying to write a Parameterized test for my add method for Binary Search tree
I wrote the test method:
void add() {
student5 = new Student("student5", LocalDate.of(2000,5,12), "none");
studentSet2.add(student5);
assertAll(
()->assertThat(studentSet2).contains(student5),
()->assertThat(studentSet2).hasSize(1),
()->assertThat(studentSet2.iterator().next()).isEqualTo(student5),
()->assertThat(studentSet2.toArray(new Student[1])).containsOnly(student5)
);
This is my current test method but i want to transform it using parameterized test.But when i started to learn about it ,i found that he can take only strings and primitiv types.
Can i write something for my test method to take a "Student" object as parameter?

You want create a lot of synthetic cases (or read from somewhere...), for example
static Stream<Arguments> testAddToAListSource() {
final List<String> baseData = List.of("one", "two", "three");
// construct cases using a base list and adding null or a new element
return IntStream.range(0, baseData.size() + 1)
.mapToObj(n -> baseData.stream().limit(n).collect(toList()))
.flatMap(xs -> Stream.of(null, "extra", "superExtra")
.map(x -> arguments(new ArrayList<>(xs), x)));
}
this method create pairs of a list of strings and a new string to add.
Suppose you want to verify that this element is correctly added to the list checking the following invariants
#ParameterizedTest
#MethodSource("testAddToAListSource")
void testAddToAList(List<String> caseList, String newElement) {
// count elements
final long currentSize = caseList.size();
// count how many times the element is in list
final long currentTimes = caseList.stream().filter(e -> Objects.equals(e, newElement)).count();
// add
caseList.add(newElement);
// count elements
final long newSize = caseList.size();
// count how many times the element is in list
final long newTimes = caseList.stream().filter(e -> Objects.equals(e, newElement)).count();
assertEquals(newSize, currentSize + 1);
assertEquals(newTimes, currentTimes + 1);
}
with output
in your specific case I don't know what type is studentSet2 but probably you could change your add signature to
#ParameterizedTest
#MethodSource("testAdd")
void add(StudentSet studentSet2, Student student5) {
...
and create a provider like
static Stream<Arguments> testAdd() {
...

Related

Filtering/Removing from a Nested List of Objects using Streams in Java

Say that we have a 3-dimensional List of Objects.
class OneDObject {
int id;
List<Integer> list;
OneDObject(int id, List<Integer>list) { // Constructor }
// Getters and Setters
}
class TwoDObject {
int id;
List<OneDObject> list;
TwoDObject(int id, List<OneDObject> list) { // Constructor }
// Getters and Setters
}
var l1 = List.of(1,2,4);
var l2 = List.of(2,4,6);
var obj1d1 = new OneDObject(1, l1);
var obj1d2 = new OneDObject(2, l2);
var l3 = List.of(obj1d1, obj1d2);
var l4 = List.of(obj1d1);
var obj2d1 = new TwoDObject(3, l3);
var obj2d2 = new TwoDObject(4, l4);
var l5 = List.of(obj2d1, obj2d2); // 3-d list
Say that I want to filter "l5" such that if any element in the inner most list is an odd number then the entire list should be deleted, and if that makes the 2nd level list as empty, then that should be deleted in return.
So, for the given example, before filtering if it is:
[[[1,2,4],[2,4,6]], [[1,2,4]]]
After filtering, it should be:
[[[2,4,6]]]
How can I do this using Streams in Java?
Since you need your lists to be updated, in the below solution I am using removeIf method of the List to remove any elements which does not meet the necessary criteria. So for removeIf to work, the list should not be immutable. So replace the var list = List.of(...) code with var list = new ArrayList<>(List.of(...));
(Note: Null checks have been ignored as well.)
Now, this problem could be split into components:
Predicate to identify if a list has any odd elements.
Predicate<OneDObject> hasOdd = obj-> obj.getList().stream().anyMatch(i -> i % 2 != 0);
Predicate to remove objects from 2d list, which has odd elements in its 1d list.
Predicate<TwoDObject> validate2d = obj -> {
// remove any 1d list that has atleast one odd number.
obj.getList().removeIf(hasOdd);
// check if there are any valid 1d lists
return obj.getList().isEmpty();
};
Now apply the predicate to the final list:
l5.removeIf(validate2d); // l5 will now contain only the 2d object having [2,4,6] list
Here's the final code (in Java, but I think it should almost be interchangeable with Kotlin)
List<TwoDObject> l6 = l5.stream()
.peek(twoDObject -> {
List<OneDObject> filteredOneDObjectList = twoDObject.getList()
.stream()
.filter(oneDObject -> oneDObject.getList()
.stream()
.noneMatch(i -> i % 2 == 1))
.toList();
twoDObject.setList(filteredOneDObjectList);
})
.filter(twoDObject -> twoDObject.getList().size() > 0)
.toList();
First we go through every twoDObject by calling Stream#peek, then stream its list and filter out every oneDObject, which contains an odd number. Then the list is saved back into the current twoDObject.
In the end we filter out all empty twoDObjects.
Note that Stream#peek should normally only be used for the purpose of debugging and not mutating the stream elements.
In this case it could also be replaced with
List<TwoDObject> l6 = l5.stream()
.map(twoDObject -> {
...
return twoDObject;
})
...

How to assert a List in Java

I have two methods in a separate class in type List(). The are both returning lists , however on my test I want to assert results of both methods. Both these methods are in a Class Called navigate. My assert statement isnt working :( - I want my test to fail if the values are the same and to pass if the values are not the same
public List<Integer> methodA() {
List<Integer> overallDurationAndTimeAfterWayPoint = new ArrayList<>();
if (routeOptions.size() != 0) {
for (int i = 0; i < routeOptionDescriptions.size(); i++) {
overallDurationAndTimeAfterWayPoint.add(Integer.parseInt(routeOptionDescriptions.get(i).getText()
.replaceAll("[^\\d.]", "").trim()));
overallDurationAndTimeAfterWayPoint.add(Integer.parseInt(routeOptionTravelTimes.get(i).getText()
.replaceAll("[^\\d.]", "").trim()));
}
}
return overallDurationAndTimeAfterWayPoint;
}
public List<Integer> Method B() {
List<Integer> overallDurationAndTimeAfterWayPoint = new ArrayList<>();
if (routeOptions.size() != 0) {
for (int i = 0; i < routeOptionDescriptions.size(); i++) {
overallDurationAndTimeAfterWayPoint.add(Integer.parseInt(routeOptionDescriptions.get(i).getText()
.replaceAll("[^\\d.]", "").trim()));
overallDurationAndTimeAfterWayPoint.add(Integer.parseInt(routeOptionTravelTimes.get(i).getText()
.replaceAll("[^\\d.]", "").trim()));
}
}
System.out.println("After " + overallDurationAndTimeAfterWayPoint);
return overallDurationAndTimeAfterWayPoint;
}
Assert.assertTrue(navigate.MethodA().equals(navigate.MethodB()));
You should do
Assert.assertFalse(navigate.MethodA().equals(navigate.MethodB()));
or
Assert.assertTrue(!navigate.MethodA().equals(navigate.MethodB()));
The assertions will throw an exception when the condition is not respected, so you need to assert that the two values are not equal. In this case, if they're not equal the assertion will pass
Does order play role or not? That is something very important if you want to state if they are "same" or not.
Also I miss information what library and what version of this library are you trying to use....
Nevetheless - you still can compare those to lists as Strings.
String listAsString = list.stream()
.map(n -> String.valueOf(n))
.collect(Collectors.joining(",", "[", "]"));
that will result into [x,y,z,...] format. If order matter, you will not sort it before (also list is not best structure for something where order matters), if order does not matter - sort it first, and then compare String representations.
If you know the exact expected result, you can
compare for the expected list length
check relevant elements from the list
If order does not matter, apply some sorting first as suggested by #Smeki.

order List<Object> with the order of List<Long> java

After reading several questions and examples I came with this example which I modified a bit to make it work as expected.
Collections.sort(listToOrder, Comparator.comparing(item -> someObject.getListOfLongs().indexOf(item.getId())));
So listToOrder is a list of MyObject which has name and id, so I need to order listToOrder in the same order as listOfLongs.
With the example of code given it work as expected however if the listToOrder is different in size it fails, wondering how I could make it to work even if the sizes are different.
Edit:
I misread, the error I was getting was an IndexOutOfBoundsException which wasn't triggered by the line of code I put up there, it was because of a manual log.
List.indexOf() returns -1 if the element is not found, which means such items will be ordered first in the resulting sorted list.
Without ordering data, the only other sensible way to handle such elements is to order them last:
Collections.sort(listToOrder, Comparator.comparing(item -> someObject.getListOfLongs().contains(item.getId()) ? someObject.getListOfLongs().indexOf(item.getId()) : Integer.MAX_VALUE));
This has nothing to do with sorting, but ordering. Having the following object with full-args constructor and getters:
public static class MyObject {
private final long id;
private final String name;
}
... and the following data in a random order ...
List<Integer> ids = Arrays.asList(5,4,7,0,2,1,3,8,6);
List<MyObject> list = Arrays.asList(
new MyObject(1, "one"),
new MyObject(3, "three"),
...
new MyObject(6, "six"),
new MyObject(8, "eight")
);
The solution you are looking for is this:
List<MyObject> newList = new ArrayList<>(list);
for (int i=0; i<ids.size(); i++) {
int id = ids.get(i);
for (MyObject myObject: list) {
if (myObject.getId() == id) {
newList.set(i, myObject);
break;
}
}
}
Simply find the object with the matching ID and set it to a new list. There is no dedicated method to do that.

How to use Java 8 Streams to Implement zip in specific way?

I'm trying to figure out how to use the Streams API to implement a zip function that takes an unbounded number of int[]'s as an argument; takes the i'th element from each; puts those in a Tuple (obviously a custom Tuple object is needed - which I have) and returns a list of Tuples (i.e. List).
Essentially, for:
{ 1, 2, 3 }
{ 4, 5, 6 }
the proposed method should return:
[ Tuple(1, 4), Tuple(2, 5), Tuple(3, 6) ] as a java.util.List<Tuple>
Here is a function that does what I'm trying to do in a "normal" way:
/**
* Return a list of tuples, where each tuple contains the i-th element
* from each of the argument sequences. The returned list is
* truncated in length to the length of the shortest argument sequence.
*
* #param args the array of ints to be wrapped in {#link Tuple}s
* #return a list of tuples
*/
public static List<Tuple> zip(int[]... args) {
List<Tuple> retVal = new ArrayList<>();
// Find the array with the minimum size
int minLength = Arrays.stream(args).map(i -> new Integer(i.length)).min((a, b) -> a.compareTo(b)).get();
for(int i = 0;i < minLength;i++) {
Tuple.Builder builder = Tuple.builder();
for(int[] ia : args) {
builder.add(ia[i]);
}
retVal.add(builder.build());
}
return retVal;
}
A solution is to create a Stream over the indexes and using mapToObj to map each int into a Tuple. Also, since you already have a Builder object, we can utilize it to collect the elements into it.
Supposing we add a method Tuple.Builder.addAll(Tuple.Builder other) whose purpose would be to add one builder to another, we could have the following code:
public static List<Tuple> zip(int[]... args) {
// Find the array with the minimum size
int minLength = Arrays.stream(args).mapToInt(i -> i.length).min().orElse(0);
return IntStream.range(0, minLength)
.mapToObj(i ->
Arrays.stream(args)
.mapToInt(ia -> ia[i])
.collect(Tuple::builder, Tuple.Builder::add, Tuple.Builder::addAll)
.build()
).collect(Collectors.toList());
}
(If you don't want to support parallel execution, you could just throw an exception with (b1, b2) -> { throw new IllegalStateException(); } and not add the addAll method.)
As a side-note, the code for finding the minimal array size can be simplified: you don't need to box into an Integer, you can just map each array to its length and get the minimum with min(). This returns an OptionalInt; instead of getting its value, which might throw an exception if the Stream was empty, I used orElse(0) so that, in the case of an empty Stream, an empty list is returned.

Using streams to convert a list of objects into a string obtained from the toString method

There are a lot of useful new things in Java 8. E.g., I can iterate with a stream over a list of objects and then sum the values from a specific field of the Object's instances. E.g.
public class AClass {
private int value;
public int getValue() { return value; }
}
Integer sum = list.stream().mapToInt(AClass::getValue).sum();
Thus, I'm asking if there is any way to build a String that concatenates the output of the toString() method from the instances in a single line.
List<Integer> list = ...
String concatenated = list.stream().... //concatenate here with toString() method from java.lang.Integer class
Suppose that list contains integers 1, 2 and 3, I expect that concatenated is "123" or "1,2,3".
One simple way is to append your list items in a StringBuilder
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
StringBuilder b = new StringBuilder();
list.forEach(b::append);
System.out.println(b);
you can also try:
String s = list.stream().map(e -> e.toString()).reduce("", String::concat);
Explanation: map converts Integer stream to String stream, then its reduced as concatenation of all the elements.
Note: This is normal reduction which performs in O(n2)
for better performance use a StringBuilder or mutable reduction similar to F. Böller's answer.
String s = list.stream().map(Object::toString).collect(Collectors.joining(","));
Ref: Stream Reduction
There is a collector joining in the API.
It's a static method in Collectors.
list.stream().map(Object::toString).collect(Collectors.joining(","))
Not perfect because of the necessary call of toString, but works. Different delimiters are possible.
Just in case anyone is trying to do this without java 8, there is a pretty good trick. List.toString() already returns a collection that looks like this:
[1,2,3]
Depending on your specific requirements, this can be post-processed to whatever you want as long as your list items don't contain [] or , .
For instance:
list.toString().replace("[","").replace("]","")
or if your data might contain square brackets this:
String s=list.toString();
s = s.substring(1,s.length()-1)
will get you a pretty reasonable output.
One array item on each line can be created like this:
list.toString().replace("[","").replace("]","").replaceAll(",","\r\n")
I used this technique to make html tooltips from a list in a small app, with something like:
list.toString().replace("[","<html>").replace("]","</html>").replaceAll(",","<br>")
If you have an array then start with Arrays.asList(list).toString() instead
I'll totally own the fact that this is not optimal, but it's not as inefficient as you might think and is pretty straightforward to read and understand. It is, however, quite inflexible--in particular don't try to separate the elements with replaceAll if your data might contain commas and use the substring version if you have square brackets in your data, but for an array of numbers it's pretty much perfect.
There is a method in the String API for those "joining list of string" usecases, you don't even need Stream.
List<String> myStringIterable = Arrays.asList("baguette", "bonjour");
String myReducedString = String.join(",", myStringIterable);
// And here you obtain "baguette,bonjour" in your myReducedString variable
The other answers are fine. However, you can also pass Collectors.toList() as parameter to Stream.collect() to return the elements as an ArrayList.
System.out.println( list.stream().map( e -> e.toString() ).collect( toList() ) );
StringListName = ObjectListName.stream().map( m -> m.toString() ).collect( Collectors.toList() );
List<String> list = Arrays.asList("One", "Two", "Three");
list.stream()
.reduce("", org.apache.commons.lang3.StringUtils::join);
Or
List<String> list = Arrays.asList("One", "Two", "Three");
list.stream()
.reduce("", (s1,s2)->s1+s2);
This approach allows you also build a string result from a list of objects
Example
List<Wrapper> list = Arrays.asList(w1, w2, w2);
list.stream()
.map(w->w.getStringValue)
.reduce("", org.apache.commons.lang3.StringUtils::join);
Here the reduce function allows you to have some initial value to which you want to append new string
Example:
List<String> errors = Arrays.asList("er1", "er2", "er3");
list.stream()
.reduce("Found next errors:", (s1,s2)->s1+s2);
Testing both approaches suggested in Shail016 and bpedroso answer (https://stackoverflow.com/a/24883180/2832140), the simple StringBuilder + append(String) within a for loop, seems to execute much faster than list.stream().map([...].
Example: This code walks through a Map<Long, List<Long>> builds a json string, using list.stream().map([...]:
if (mapSize > 0) {
StringBuilder sb = new StringBuilder("[");
for (Map.Entry<Long, List<Long>> entry : threadsMap.entrySet()) {
sb.append("{\"" + entry.getKey().toString() + "\":[");
sb.append(entry.getValue().stream().map(Object::toString).collect(Collectors.joining(",")));
}
sb.delete(sb.length()-2, sb.length());
sb.append("]");
System.out.println(sb.toString());
}
On my dev VM, junit usually takes between 0.35 and 1.2 seconds to execute the test. While, using this following code, it takes between 0.15 and 0.33 seconds:
if (mapSize > 0) {
StringBuilder sb = new StringBuilder("[");
for (Map.Entry<Long, List<Long>> entry : threadsMap.entrySet()) {
sb.append("{\"" + entry.getKey().toString() + "\":[");
for (Long tid : entry.getValue()) {
sb.append(tid.toString() + ", ");
}
sb.delete(sb.length()-2, sb.length());
sb.append("]}, ");
}
sb.delete(sb.length()-2, sb.length());
sb.append("]");
System.out.println(sb.toString());
}
A clean way to do this is by mapping the elements of the list to string and then using the joining operation in Collectors class.
List<Integer> ls = new ArrayList<Integer>();
ls.add(1);
ls.add(2);
ls.add(3);
String s = ls.stream().map(Object::toString).collect(Collectors.joining(","));
String actual = list.stream().reduce((t, u) -> t + "," + u).get();
I'm going to use the streams api to convert a stream of integers into a single string. The problem with some of the provided answers is that they produce a O(n^2) runtime because of String building. A better solution is to use a StringBuilder, and then join the strings together as the final step.
// Create a stream of integers
String result = Arrays.stream(new int[]{1,2,3,4,5,6 })
// collect into a single StringBuilder
.collect(StringBuilder::new, // supplier function
// accumulator - converts cur integer into a string and appends it to the string builder
(builder, cur) -> builder.append(Integer.toString(cur)),
// combiner - combines two string builders if running in parallel
StringBuilder::append)
// convert StringBuilder into a single string
.toString();
You can take this process a step further by converting the collection of object to a single string.
// Start with a class definition
public static class AClass {
private int value;
public int getValue() { return value; }
public AClass(int value) { this.value = value; }
#Override
public String toString() {
return Integer.toString(value);
}
}
// Create a stream of AClass objects
String resultTwo = Arrays.stream(new AClass[]{
new AClass(1),
new AClass(2),
new AClass(3),
new AClass(4)
})
// transform stream of objects into a single string
.collect(StringBuilder::new,
(builder, curObj) -> builder.append(curObj.toString()),
StringBuilder::append
)
// finally transform string builder into a single string
.toString();
Can we try this.
public static void main(String []args){
List<String> stringList = new ArrayList<>();
for(int i=0;i< 10;i++){
stringList.add(""+i);
}
String stringConcated = String.join(",", stringList);
System.out.println(stringConcated);
}
Also, you can do like this.
List<String> list = Arrays.asList("One", "Two", "Three");
String result = String.join(", ", list);
System.out.println(result);
With Java 8+
String s = Arrays.toString(list.stream().toArray(AClass[]::new));
Not the most efficient, but it is a solution with a small amount of code.

Categories